@nucleux/knowledge-base 0.0.0-stage → 0.7.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/LICENSE +21 -0
- package/README.md +38 -2
- package/dist/index.cjs +290 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +96 -0
- package/dist/index.d.ts +96 -0
- package/dist/index.js +266 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nucleux
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,39 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @nucleux/knowledge-base
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
KnowledgeBase — select the context files an agent may use — Nucleux agentic UI for React.
|
|
4
|
+
|
|
5
|
+
Part of **[Nucleux](https://github.com/intelligaia-open-labs/nucleux)** — agentic UI components for React. Install a single component, or the full kit via [`@nucleux/react`](https://www.npmjs.com/package/@nucleux/react).
|
|
6
|
+
|
|
7
|
+
## Exports
|
|
8
|
+
|
|
9
|
+
- **`KnowledgeBase`** (variant `kb-picker`) — choose which bases the agent grounds in this session. Props: `sources`, `value` / `defaultValue` / `onValueChange`, `heading`, plus `variant` (`"list"` default, or `"chips"` — "Product docs · 412"), `onAdd` / `addLabel` ("Add a base"), `onRefresh(id)` (Refresh / Retry on a stale or failed row).
|
|
10
|
+
`KnowledgeSource`: `id`, `name`, `meta`, `icon`, plus `status` (`"ready"` | `"processing"` | `"stale"` | `"error"`, shown as Ready, Processing, Needs refresh, Couldn't process) and `count`.
|
|
11
|
+
- **`KnowledgeBaseSources`** (variant `kb-source-list`) — under an answer: the documents and sections it read (`sources: { id, name, section?, href? }[]`), "Used 3 sources from Sales playbook" with `baseName`; `reading` shows and announces "Reading…" while it happens.
|
|
12
|
+
- **`KnowledgeBaseCoverage`** (variant `kb-coverage-badge`) — `level` (`"high"` | `"partial"` | `"low"`) and `matches`: "Well-covered · 14 matches", "Partly covered", "Thin coverage".
|
|
13
|
+
- Types: `KnowledgeBaseProps`, `KnowledgeBaseVariant`, `KnowledgeSource`, `KnowledgeSourceStatus`, `KnowledgeBaseSourceRef`, `KnowledgeBaseSourcesProps`, `KnowledgeBaseCoverageProps`, `KnowledgeBaseCoverageLevel`.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @nucleux/knowledge-base @nucleux/tokens
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`react` / `react-dom` are peer dependencies.
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { KnowledgeBase, KnowledgeBaseSources, KnowledgeBaseCoverage } from "@nucleux/knowledge-base";
|
|
27
|
+
|
|
28
|
+
<KnowledgeBase variant="chips" heading="Grounded in" sources={bases} value={active} onValueChange={setActive} onAdd={addBase} />
|
|
29
|
+
|
|
30
|
+
<KnowledgeBaseSources sources={[{ id: "1", name: "Renewal Playbook v3", section: "§4.1" }]} />
|
|
31
|
+
<KnowledgeBaseCoverage level="high" matches={14} />
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Requires the theme once in your app: `import "@nucleux/tokens/styles.css";`
|
|
35
|
+
and the Tailwind preset from `@nucleux/tokens/preset`.
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
MIT © Nucleux
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var React = require('react');
|
|
5
|
+
var utils = require('@nucleux/utils');
|
|
6
|
+
var badge = require('@nucleux/badge');
|
|
7
|
+
var button = require('@nucleux/button');
|
|
8
|
+
var spinner = require('@nucleux/spinner');
|
|
9
|
+
var lucideReact = require('lucide-react');
|
|
10
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
11
|
+
|
|
12
|
+
function _interopNamespace(e) {
|
|
13
|
+
if (e && e.__esModule) return e;
|
|
14
|
+
var n = Object.create(null);
|
|
15
|
+
if (e) {
|
|
16
|
+
Object.keys(e).forEach(function (k) {
|
|
17
|
+
if (k !== 'default') {
|
|
18
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
19
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () { return e[k]; }
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
n.default = e;
|
|
27
|
+
return Object.freeze(n);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
31
|
+
|
|
32
|
+
// src/index.tsx
|
|
33
|
+
var statusWord = {
|
|
34
|
+
ready: "Ready",
|
|
35
|
+
processing: "Processing",
|
|
36
|
+
stale: "Needs refresh",
|
|
37
|
+
error: "Couldn't process"
|
|
38
|
+
};
|
|
39
|
+
var statusTone = {
|
|
40
|
+
ready: "text-success",
|
|
41
|
+
processing: "text-muted-foreground",
|
|
42
|
+
stale: "text-warning",
|
|
43
|
+
error: "text-destructive"
|
|
44
|
+
};
|
|
45
|
+
var nameText = (n, fallback) => typeof n === "string" ? n : fallback;
|
|
46
|
+
var KnowledgeBase = React__namespace.forwardRef(
|
|
47
|
+
({
|
|
48
|
+
sources,
|
|
49
|
+
value,
|
|
50
|
+
defaultValue = [],
|
|
51
|
+
onValueChange,
|
|
52
|
+
heading = "Knowledge sources",
|
|
53
|
+
variant = "list",
|
|
54
|
+
onAdd,
|
|
55
|
+
addLabel = "Add a base",
|
|
56
|
+
onRefresh,
|
|
57
|
+
className,
|
|
58
|
+
...props
|
|
59
|
+
}, ref) => {
|
|
60
|
+
const [internal, setInternal] = React__namespace.useState(defaultValue);
|
|
61
|
+
const selected = value !== void 0 ? value : internal;
|
|
62
|
+
const headingId = React__namespace.useId();
|
|
63
|
+
const toggle = (id) => {
|
|
64
|
+
const next = selected.includes(id) ? selected.filter((v) => v !== id) : [...selected, id];
|
|
65
|
+
if (value === void 0) setInternal(next);
|
|
66
|
+
onValueChange?.(next);
|
|
67
|
+
};
|
|
68
|
+
if (variant === "chips") {
|
|
69
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
70
|
+
"div",
|
|
71
|
+
{
|
|
72
|
+
ref,
|
|
73
|
+
role: "group",
|
|
74
|
+
"aria-labelledby": heading ? headingId : void 0,
|
|
75
|
+
"aria-label": heading ? void 0 : "Knowledge sources",
|
|
76
|
+
className: utils.cn("flex flex-col gap-2 rounded-xl border border-border bg-background p-3", className),
|
|
77
|
+
...props,
|
|
78
|
+
children: [
|
|
79
|
+
heading && /* @__PURE__ */ jsxRuntime.jsx("p", { id: headingId, className: "text-xs font-semibold text-foreground", children: heading }),
|
|
80
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-1.5", children: [
|
|
81
|
+
sources.map((source) => {
|
|
82
|
+
const active = selected.includes(source.id);
|
|
83
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
84
|
+
"button",
|
|
85
|
+
{
|
|
86
|
+
type: "button",
|
|
87
|
+
role: "checkbox",
|
|
88
|
+
"aria-checked": active,
|
|
89
|
+
onClick: () => toggle(source.id),
|
|
90
|
+
className: utils.cn(
|
|
91
|
+
"inline-flex items-center gap-1 rounded-full border px-2.5 py-1 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-3",
|
|
92
|
+
active ? "border-brand bg-brand-muted/50 text-brand" : "border-border text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
93
|
+
),
|
|
94
|
+
children: [
|
|
95
|
+
active && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { "aria-hidden": "true" }),
|
|
96
|
+
source.name,
|
|
97
|
+
source.count != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-normal", children: [
|
|
98
|
+
"\xB7 ",
|
|
99
|
+
source.count.toLocaleString()
|
|
100
|
+
] }),
|
|
101
|
+
source.status && source.status !== "ready" && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: utils.cn("font-normal", statusTone[source.status]), children: [
|
|
102
|
+
"\xB7 ",
|
|
103
|
+
statusWord[source.status]
|
|
104
|
+
] })
|
|
105
|
+
]
|
|
106
|
+
},
|
|
107
|
+
source.id
|
|
108
|
+
);
|
|
109
|
+
}),
|
|
110
|
+
onAdd && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
111
|
+
"button",
|
|
112
|
+
{
|
|
113
|
+
type: "button",
|
|
114
|
+
onClick: onAdd,
|
|
115
|
+
className: "inline-flex items-center gap-1 rounded-full border border-dashed border-border px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-3",
|
|
116
|
+
children: [
|
|
117
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { "aria-hidden": "true" }),
|
|
118
|
+
addLabel
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
] })
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
128
|
+
"div",
|
|
129
|
+
{
|
|
130
|
+
ref,
|
|
131
|
+
className: utils.cn("flex flex-col gap-2 rounded-xl border border-border bg-background p-3", className),
|
|
132
|
+
...props,
|
|
133
|
+
children: [
|
|
134
|
+
heading && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-1", children: [
|
|
135
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { id: headingId, className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: heading }),
|
|
136
|
+
selected.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
137
|
+
selected.length,
|
|
138
|
+
" selected"
|
|
139
|
+
] })
|
|
140
|
+
] }),
|
|
141
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
142
|
+
"ul",
|
|
143
|
+
{
|
|
144
|
+
"aria-labelledby": heading ? headingId : void 0,
|
|
145
|
+
"aria-label": heading ? void 0 : "Knowledge sources",
|
|
146
|
+
className: "flex flex-col gap-1",
|
|
147
|
+
children: sources.map((source) => {
|
|
148
|
+
const active = selected.includes(source.id);
|
|
149
|
+
const recover = onRefresh && (source.status === "stale" || source.status === "error") ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
150
|
+
button.Button,
|
|
151
|
+
{
|
|
152
|
+
variant: "ghost",
|
|
153
|
+
size: "sm",
|
|
154
|
+
onClick: () => onRefresh(source.id),
|
|
155
|
+
"aria-label": `${source.status === "error" ? "Retry" : "Refresh"} ${nameText(source.name, source.id)}`,
|
|
156
|
+
leftIcon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCw, { "aria-hidden": "true", className: "size-3.5" }),
|
|
157
|
+
className: "h-7 shrink-0 px-2 text-xs",
|
|
158
|
+
children: source.status === "error" ? "Retry" : "Refresh"
|
|
159
|
+
}
|
|
160
|
+
) : null;
|
|
161
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center gap-1", "data-status": source.status, children: [
|
|
162
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
163
|
+
"button",
|
|
164
|
+
{
|
|
165
|
+
type: "button",
|
|
166
|
+
role: "checkbox",
|
|
167
|
+
"aria-checked": active,
|
|
168
|
+
onClick: () => toggle(source.id),
|
|
169
|
+
className: utils.cn(
|
|
170
|
+
"flex w-full min-w-0 flex-1 items-center gap-3 rounded-lg border px-3 py-2 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
171
|
+
active ? "border-brand bg-brand-muted/40" : "border-transparent hover:bg-accent"
|
|
172
|
+
),
|
|
173
|
+
children: [
|
|
174
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex size-8 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground [&_svg]:size-4", children: source.icon ?? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { "aria-hidden": "true" }) }),
|
|
175
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
176
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-medium text-foreground", children: source.name }),
|
|
177
|
+
(source.meta || source.status || source.count != null) && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate text-xs text-muted-foreground", children: [
|
|
178
|
+
source.status && /* @__PURE__ */ jsxRuntime.jsx("span", { className: utils.cn("font-medium", statusTone[source.status]), children: statusWord[source.status] }),
|
|
179
|
+
source.status && (source.meta || source.count != null) && " \xB7 ",
|
|
180
|
+
source.count != null && `${source.count.toLocaleString()} docs`,
|
|
181
|
+
source.count != null && source.meta && " \xB7 ",
|
|
182
|
+
source.meta
|
|
183
|
+
] })
|
|
184
|
+
] }),
|
|
185
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
186
|
+
"span",
|
|
187
|
+
{
|
|
188
|
+
className: utils.cn(
|
|
189
|
+
"inline-flex size-5 shrink-0 items-center justify-center rounded-md border transition-colors [&_svg]:size-3.5",
|
|
190
|
+
active ? "border-brand bg-brand text-brand-foreground" : "border-border"
|
|
191
|
+
),
|
|
192
|
+
children: active && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { "aria-hidden": "true" })
|
|
193
|
+
}
|
|
194
|
+
)
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
),
|
|
198
|
+
recover
|
|
199
|
+
] }, source.id);
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
),
|
|
203
|
+
onAdd && /* @__PURE__ */ jsxRuntime.jsx(button.Button, { variant: "ghost", size: "sm", onClick: onAdd, leftIcon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { "aria-hidden": "true", className: "size-4" }), className: "self-start", children: addLabel })
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
);
|
|
209
|
+
KnowledgeBase.displayName = "KnowledgeBase";
|
|
210
|
+
var KnowledgeBaseSources = React__namespace.forwardRef(
|
|
211
|
+
({ sources, baseName, heading, reading = false, className, ...props }, ref) => {
|
|
212
|
+
const headingId = React__namespace.useId();
|
|
213
|
+
const n = sources.length;
|
|
214
|
+
const count = `${n} ${n === 1 ? "source" : "sources"}`;
|
|
215
|
+
const title = heading ?? (reading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
216
|
+
"Reading",
|
|
217
|
+
baseName ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
218
|
+
" ",
|
|
219
|
+
baseName
|
|
220
|
+
] }) : null,
|
|
221
|
+
"\u2026"
|
|
222
|
+
] }) : baseName ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
223
|
+
"Used ",
|
|
224
|
+
count,
|
|
225
|
+
" from ",
|
|
226
|
+
baseName
|
|
227
|
+
] }) : count);
|
|
228
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: utils.cn("flex flex-col gap-1.5", className), ...props, children: [
|
|
229
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
230
|
+
"p",
|
|
231
|
+
{
|
|
232
|
+
id: headingId,
|
|
233
|
+
role: "status",
|
|
234
|
+
"aria-live": "polite",
|
|
235
|
+
className: "inline-flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground",
|
|
236
|
+
children: [
|
|
237
|
+
reading && /* @__PURE__ */ jsxRuntime.jsx(spinner.Spinner, { "aria-hidden": "true", role: void 0, className: "size-3 motion-reduce:animate-none" }),
|
|
238
|
+
title
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
),
|
|
242
|
+
n > 0 && /* @__PURE__ */ jsxRuntime.jsx("ul", { "aria-labelledby": headingId, className: "flex flex-col gap-1", children: sources.map((s) => {
|
|
243
|
+
const body = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
244
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { "aria-hidden": "true", className: "size-3.5 shrink-0 text-muted-foreground" }),
|
|
245
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: s.name }),
|
|
246
|
+
s.section && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "shrink-0 text-muted-foreground", children: [
|
|
247
|
+
"\xB7 ",
|
|
248
|
+
s.section
|
|
249
|
+
] })
|
|
250
|
+
] });
|
|
251
|
+
const cls = "flex items-center gap-2 rounded-md bg-muted/60 px-2.5 py-1.5 text-xs text-foreground";
|
|
252
|
+
return /* @__PURE__ */ jsxRuntime.jsx("li", { children: s.href ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
253
|
+
"a",
|
|
254
|
+
{
|
|
255
|
+
href: s.href,
|
|
256
|
+
className: utils.cn(cls, "transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"),
|
|
257
|
+
children: body
|
|
258
|
+
}
|
|
259
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: cls, children: body }) }, s.id);
|
|
260
|
+
}) })
|
|
261
|
+
] });
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
KnowledgeBaseSources.displayName = "KnowledgeBaseSources";
|
|
265
|
+
var coverage = {
|
|
266
|
+
high: { word: "Well-covered", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { "aria-hidden": "true", className: "size-3" }), cls: "bg-success/15 text-success" },
|
|
267
|
+
partial: { word: "Partly covered", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { "aria-hidden": "true", className: "size-3" }), cls: "bg-warning/15 text-warning" },
|
|
268
|
+
low: { word: "Thin coverage", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleAlert, { "aria-hidden": "true", className: "size-3" }), cls: "bg-destructive/15 text-destructive" }
|
|
269
|
+
};
|
|
270
|
+
var KnowledgeBaseCoverage = React__namespace.forwardRef(
|
|
271
|
+
({ level, matches, label, className, ...props }, ref) => {
|
|
272
|
+
const c = coverage[level];
|
|
273
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(badge.Badge, { ref, variant: "outline", icon: c.icon, "data-level": level, className: utils.cn("border-transparent", c.cls, className), ...props, children: [
|
|
274
|
+
label ?? c.word,
|
|
275
|
+
matches != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-normal", children: [
|
|
276
|
+
"\xB7 ",
|
|
277
|
+
matches,
|
|
278
|
+
" ",
|
|
279
|
+
matches === 1 ? "match" : "matches"
|
|
280
|
+
] })
|
|
281
|
+
] });
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
KnowledgeBaseCoverage.displayName = "KnowledgeBaseCoverage";
|
|
285
|
+
|
|
286
|
+
exports.KnowledgeBase = KnowledgeBase;
|
|
287
|
+
exports.KnowledgeBaseCoverage = KnowledgeBaseCoverage;
|
|
288
|
+
exports.KnowledgeBaseSources = KnowledgeBaseSources;
|
|
289
|
+
//# sourceMappingURL=index.cjs.map
|
|
290
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"names":["React","jsxs","cn","jsx","Check","Plus","Button","RotateCw","FileText","Fragment","Spinner","AlertTriangle","CircleAlert","Badge"],"mappings":"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","file":"index.cjs","sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@nucleux/utils\";\nimport { Badge } from \"@nucleux/badge\";\nimport { Button } from \"@nucleux/button\";\nimport { Spinner } from \"@nucleux/spinner\";\nimport { AlertTriangle, Check, CircleAlert, FileText, Plus, RotateCw } from \"lucide-react\";\n\n/** Picker layout: a vertical list of rows, or a wrap of chips with document counts. */\nexport type KnowledgeBaseVariant = \"list\" | \"chips\";\n\n/** Whether a source is usable yet, and whether it is still current. */\nexport type KnowledgeSourceStatus = \"ready\" | \"processing\" | \"stale\" | \"error\";\n\n/** How well the knowledge base covers the question. */\nexport type KnowledgeBaseCoverageLevel = \"high\" | \"partial\" | \"low\";\n\nexport interface KnowledgeSource {\n /** Stable id. */\n id: string;\n /** Source name, e.g. a filename. */\n name: React.ReactNode;\n /** Metadata line, e.g. `\"PDF · 12 pages\"`. */\n meta?: React.ReactNode;\n /** Leading icon. Defaults to a document glyph. */\n icon?: React.ReactNode;\n /** Readiness, shown as a word on the row. Omit for plain sources. */\n status?: KnowledgeSourceStatus;\n /** Number of documents in the base, shown on chips: `\"Product docs · 412\"`. */\n count?: number;\n}\n\nexport interface KnowledgeBaseProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n /** The available knowledge sources. */\n sources: KnowledgeSource[];\n /** Selected source ids (controlled). */\n value?: string[];\n /** Initial selection (uncontrolled). */\n defaultValue?: string[];\n /** Called when the selection changes. */\n onValueChange?: (ids: string[]) => void;\n /** Heading above the list. Defaults to `\"Knowledge sources\"`. */\n heading?: React.ReactNode;\n /** Layout. Defaults to `\"list\"`; `\"chips\"` is the compact \"Grounded in\" picker. */\n variant?: KnowledgeBaseVariant;\n /** Adds an \"Add a base\" control. */\n onAdd?: () => void;\n /** Label for the add control. Defaults to `\"Add a base\"`. */\n addLabel?: React.ReactNode;\n /** Recovery for a stale or failed source; renders Refresh / Retry on that row. */\n onRefresh?: (id: string) => void;\n}\n\nconst statusWord: Record<KnowledgeSourceStatus, string> = {\n ready: \"Ready\",\n processing: \"Processing\",\n stale: \"Needs refresh\",\n error: \"Couldn't process\",\n};\n\nconst statusTone: Record<KnowledgeSourceStatus, string> = {\n ready: \"text-success\",\n processing: \"text-muted-foreground\",\n stale: \"text-warning\",\n error: \"text-destructive\",\n};\n\nconst nameText = (n: React.ReactNode, fallback: string) => (typeof n === \"string\" ? n : fallback);\n\n/**\n * Lets people choose which knowledge bases the agent grounds in for this\n * session — a list of checkable rows (default) or a compact wrap of chips with\n * document counts (`variant=\"chips\"`). Each source can carry its readiness as a\n * word (Ready, Processing, Needs refresh, Couldn't process) with the recovery\n * on the row that failed. Pair it with KnowledgeBaseSources (what an answer\n * actually read) and KnowledgeBaseCoverage (how well the base covers it).\n * Source: Figma \"Context Expansion / Knowledge Bases\".\n */\nexport const KnowledgeBase = React.forwardRef<HTMLDivElement, KnowledgeBaseProps>(\n (\n {\n sources,\n value,\n defaultValue = [],\n onValueChange,\n heading = \"Knowledge sources\",\n variant = \"list\",\n onAdd,\n addLabel = \"Add a base\",\n onRefresh,\n className,\n ...props\n },\n ref,\n ) => {\n const [internal, setInternal] = React.useState<string[]>(defaultValue);\n const selected = value !== undefined ? value : internal;\n const headingId = React.useId();\n const toggle = (id: string) => {\n const next = selected.includes(id) ? selected.filter((v) => v !== id) : [...selected, id];\n if (value === undefined) setInternal(next);\n onValueChange?.(next);\n };\n\n if (variant === \"chips\") {\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-labelledby={heading ? headingId : undefined}\n aria-label={heading ? undefined : \"Knowledge sources\"}\n className={cn(\"flex flex-col gap-2 rounded-xl border border-border bg-background p-3\", className)}\n {...props}\n >\n {heading && (\n <p id={headingId} className=\"text-xs font-semibold text-foreground\">\n {heading}\n </p>\n )}\n <div className=\"flex flex-wrap gap-1.5\">\n {sources.map((source) => {\n const active = selected.includes(source.id);\n return (\n <button\n key={source.id}\n type=\"button\"\n role=\"checkbox\"\n aria-checked={active}\n onClick={() => toggle(source.id)}\n className={cn(\n \"inline-flex items-center gap-1 rounded-full border px-2.5 py-1 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-3\",\n active\n ? \"border-brand bg-brand-muted/50 text-brand\"\n : \"border-border text-muted-foreground hover:bg-accent hover:text-foreground\",\n )}\n >\n {active && <Check aria-hidden=\"true\" />}\n {source.name}\n {source.count != null && <span className=\"font-normal\">· {source.count.toLocaleString()}</span>}\n {source.status && source.status !== \"ready\" && (\n <span className={cn(\"font-normal\", statusTone[source.status])}>· {statusWord[source.status]}</span>\n )}\n </button>\n );\n })}\n {onAdd && (\n <button\n type=\"button\"\n onClick={onAdd}\n className=\"inline-flex items-center gap-1 rounded-full border border-dashed border-border px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-3\"\n >\n <Plus aria-hidden=\"true\" />\n {addLabel}\n </button>\n )}\n </div>\n </div>\n );\n }\n\n return (\n <div\n ref={ref}\n className={cn(\"flex flex-col gap-2 rounded-xl border border-border bg-background p-3\", className)}\n {...props}\n >\n {heading && (\n <div className=\"flex items-center justify-between px-1\">\n <p id={headingId} className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {heading}\n </p>\n {selected.length > 0 && (\n <span className=\"text-xs text-muted-foreground\">{selected.length} selected</span>\n )}\n </div>\n )}\n <ul\n aria-labelledby={heading ? headingId : undefined}\n aria-label={heading ? undefined : \"Knowledge sources\"}\n className=\"flex flex-col gap-1\"\n >\n {sources.map((source) => {\n const active = selected.includes(source.id);\n const recover =\n onRefresh && (source.status === \"stale\" || source.status === \"error\") ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => onRefresh(source.id)}\n aria-label={`${source.status === \"error\" ? \"Retry\" : \"Refresh\"} ${nameText(source.name, source.id)}`}\n leftIcon={<RotateCw aria-hidden=\"true\" className=\"size-3.5\" />}\n className=\"h-7 shrink-0 px-2 text-xs\"\n >\n {source.status === \"error\" ? \"Retry\" : \"Refresh\"}\n </Button>\n ) : null;\n return (\n <li key={source.id} className=\"flex items-center gap-1\" data-status={source.status}>\n <button\n type=\"button\"\n role=\"checkbox\"\n aria-checked={active}\n onClick={() => toggle(source.id)}\n className={cn(\n \"flex w-full min-w-0 flex-1 items-center gap-3 rounded-lg border px-3 py-2 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n active ? \"border-brand bg-brand-muted/40\" : \"border-transparent hover:bg-accent\",\n )}\n >\n <span className=\"inline-flex size-8 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground [&_svg]:size-4\">\n {source.icon ?? <FileText aria-hidden=\"true\" />}\n </span>\n <span className=\"flex min-w-0 flex-1 flex-col\">\n <span className=\"truncate text-sm font-medium text-foreground\">{source.name}</span>\n {(source.meta || source.status || source.count != null) && (\n <span className=\"truncate text-xs text-muted-foreground\">\n {source.status && (\n <span className={cn(\"font-medium\", statusTone[source.status])}>\n {statusWord[source.status]}\n </span>\n )}\n {source.status && (source.meta || source.count != null) && \" · \"}\n {source.count != null && `${source.count.toLocaleString()} docs`}\n {source.count != null && source.meta && \" · \"}\n {source.meta}\n </span>\n )}\n </span>\n <span\n className={cn(\n \"inline-flex size-5 shrink-0 items-center justify-center rounded-md border transition-colors [&_svg]:size-3.5\",\n active ? \"border-brand bg-brand text-brand-foreground\" : \"border-border\",\n )}\n >\n {active && <Check aria-hidden=\"true\" />}\n </span>\n </button>\n {recover}\n </li>\n );\n })}\n </ul>\n {onAdd && (\n <Button variant=\"ghost\" size=\"sm\" onClick={onAdd} leftIcon={<Plus aria-hidden=\"true\" className=\"size-4\" />} className=\"self-start\">\n {addLabel}\n </Button>\n )}\n </div>\n );\n },\n);\nKnowledgeBase.displayName = \"KnowledgeBase\";\n\n/** One document an answer drew on. */\nexport interface KnowledgeBaseSourceRef {\n /** Stable id. */\n id: string;\n /** Document title, e.g. `\"Renewal Playbook v3\"`. */\n name: React.ReactNode;\n /** Section the answer came from, e.g. `\"§4.1\"`. */\n section?: React.ReactNode;\n /** Link to open the document at that section. */\n href?: string;\n}\n\nexport interface KnowledgeBaseSourcesProps extends React.HTMLAttributes<HTMLDivElement> {\n /** The documents the answer read. */\n sources: KnowledgeBaseSourceRef[];\n /** The base they came from, for the heading: \"Used 3 sources from Sales playbook\". */\n baseName?: React.ReactNode;\n /** Heading override. Defaults to `\"N sources\"` (or the base sentence). */\n heading?: React.ReactNode;\n /** The agent is reading right now: shows \"Reading…\" and announces it. */\n reading?: boolean;\n}\n\n/**\n * The source list shown under an answer: exactly which knowledge-base\n * documents (and sections) the agent read, so the answer can be checked.\n * While `reading` it says so — politely announced — and swaps to provenance\n * when the answer finishes.\n */\nexport const KnowledgeBaseSources = React.forwardRef<HTMLDivElement, KnowledgeBaseSourcesProps>(\n ({ sources, baseName, heading, reading = false, className, ...props }, ref) => {\n const headingId = React.useId();\n const n = sources.length;\n const count = `${n} ${n === 1 ? \"source\" : \"sources\"}`;\n const title =\n heading ??\n (reading ? (\n <>Reading{baseName ? <> {baseName}</> : null}…</>\n ) : baseName ? (\n <>\n Used {count} from {baseName}\n </>\n ) : (\n count\n ));\n return (\n <div ref={ref} className={cn(\"flex flex-col gap-1.5\", className)} {...props}>\n <p\n id={headingId}\n role=\"status\"\n aria-live=\"polite\"\n className=\"inline-flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground\"\n >\n {reading && <Spinner aria-hidden=\"true\" role={undefined} className=\"size-3 motion-reduce:animate-none\" />}\n {title}\n </p>\n {n > 0 && (\n <ul aria-labelledby={headingId} className=\"flex flex-col gap-1\">\n {sources.map((s) => {\n const body = (\n <>\n <FileText aria-hidden=\"true\" className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{s.name}</span>\n {s.section && <span className=\"shrink-0 text-muted-foreground\">· {s.section}</span>}\n </>\n );\n const cls =\n \"flex items-center gap-2 rounded-md bg-muted/60 px-2.5 py-1.5 text-xs text-foreground\";\n return (\n <li key={s.id}>\n {s.href ? (\n <a\n href={s.href}\n className={cn(cls, \"transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\")}\n >\n {body}\n </a>\n ) : (\n <span className={cls}>{body}</span>\n )}\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n },\n);\nKnowledgeBaseSources.displayName = \"KnowledgeBaseSources\";\n\nexport interface KnowledgeBaseCoverageProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** How well the base covers the question. */\n level: KnowledgeBaseCoverageLevel;\n /** Number of matching passages, e.g. `14`. */\n matches?: number;\n /** Label override. Defaults to \"Well-covered\", \"Partly covered\" or \"Thin coverage\". */\n label?: React.ReactNode;\n}\n\nconst coverage: Record<KnowledgeBaseCoverageLevel, { word: string; icon: React.ReactNode; cls: string }> = {\n high: { word: \"Well-covered\", icon: <Check aria-hidden=\"true\" className=\"size-3\" />, cls: \"bg-success/15 text-success\" },\n partial: { word: \"Partly covered\", icon: <AlertTriangle aria-hidden=\"true\" className=\"size-3\" />, cls: \"bg-warning/15 text-warning\" },\n low: { word: \"Thin coverage\", icon: <CircleAlert aria-hidden=\"true\" className=\"size-3\" />, cls: \"bg-destructive/15 text-destructive\" },\n};\n\n/**\n * A small badge saying how well the knowledge base covers the question —\n * \"Well-covered · 14 matches\", or a warning when context is thin. The word\n * carries the meaning; colour only reinforces it.\n */\nexport const KnowledgeBaseCoverage = React.forwardRef<HTMLSpanElement, KnowledgeBaseCoverageProps>(\n ({ level, matches, label, className, ...props }, ref) => {\n const c = coverage[level];\n return (\n <Badge ref={ref} variant=\"outline\" icon={c.icon} data-level={level} className={cn(\"border-transparent\", c.cls, className)} {...props}>\n {label ?? c.word}\n {matches != null && <span className=\"font-normal\">· {matches} {matches === 1 ? \"match\" : \"matches\"}</span>}\n </Badge>\n );\n },\n);\nKnowledgeBaseCoverage.displayName = \"KnowledgeBaseCoverage\";\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/** Picker layout: a vertical list of rows, or a wrap of chips with document counts. */
|
|
4
|
+
type KnowledgeBaseVariant = "list" | "chips";
|
|
5
|
+
/** Whether a source is usable yet, and whether it is still current. */
|
|
6
|
+
type KnowledgeSourceStatus = "ready" | "processing" | "stale" | "error";
|
|
7
|
+
/** How well the knowledge base covers the question. */
|
|
8
|
+
type KnowledgeBaseCoverageLevel = "high" | "partial" | "low";
|
|
9
|
+
interface KnowledgeSource {
|
|
10
|
+
/** Stable id. */
|
|
11
|
+
id: string;
|
|
12
|
+
/** Source name, e.g. a filename. */
|
|
13
|
+
name: React.ReactNode;
|
|
14
|
+
/** Metadata line, e.g. `"PDF · 12 pages"`. */
|
|
15
|
+
meta?: React.ReactNode;
|
|
16
|
+
/** Leading icon. Defaults to a document glyph. */
|
|
17
|
+
icon?: React.ReactNode;
|
|
18
|
+
/** Readiness, shown as a word on the row. Omit for plain sources. */
|
|
19
|
+
status?: KnowledgeSourceStatus;
|
|
20
|
+
/** Number of documents in the base, shown on chips: `"Product docs · 412"`. */
|
|
21
|
+
count?: number;
|
|
22
|
+
}
|
|
23
|
+
interface KnowledgeBaseProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
|
|
24
|
+
/** The available knowledge sources. */
|
|
25
|
+
sources: KnowledgeSource[];
|
|
26
|
+
/** Selected source ids (controlled). */
|
|
27
|
+
value?: string[];
|
|
28
|
+
/** Initial selection (uncontrolled). */
|
|
29
|
+
defaultValue?: string[];
|
|
30
|
+
/** Called when the selection changes. */
|
|
31
|
+
onValueChange?: (ids: string[]) => void;
|
|
32
|
+
/** Heading above the list. Defaults to `"Knowledge sources"`. */
|
|
33
|
+
heading?: React.ReactNode;
|
|
34
|
+
/** Layout. Defaults to `"list"`; `"chips"` is the compact "Grounded in" picker. */
|
|
35
|
+
variant?: KnowledgeBaseVariant;
|
|
36
|
+
/** Adds an "Add a base" control. */
|
|
37
|
+
onAdd?: () => void;
|
|
38
|
+
/** Label for the add control. Defaults to `"Add a base"`. */
|
|
39
|
+
addLabel?: React.ReactNode;
|
|
40
|
+
/** Recovery for a stale or failed source; renders Refresh / Retry on that row. */
|
|
41
|
+
onRefresh?: (id: string) => void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Lets people choose which knowledge bases the agent grounds in for this
|
|
45
|
+
* session — a list of checkable rows (default) or a compact wrap of chips with
|
|
46
|
+
* document counts (`variant="chips"`). Each source can carry its readiness as a
|
|
47
|
+
* word (Ready, Processing, Needs refresh, Couldn't process) with the recovery
|
|
48
|
+
* on the row that failed. Pair it with KnowledgeBaseSources (what an answer
|
|
49
|
+
* actually read) and KnowledgeBaseCoverage (how well the base covers it).
|
|
50
|
+
* Source: Figma "Context Expansion / Knowledge Bases".
|
|
51
|
+
*/
|
|
52
|
+
declare const KnowledgeBase: React.ForwardRefExoticComponent<KnowledgeBaseProps & React.RefAttributes<HTMLDivElement>>;
|
|
53
|
+
/** One document an answer drew on. */
|
|
54
|
+
interface KnowledgeBaseSourceRef {
|
|
55
|
+
/** Stable id. */
|
|
56
|
+
id: string;
|
|
57
|
+
/** Document title, e.g. `"Renewal Playbook v3"`. */
|
|
58
|
+
name: React.ReactNode;
|
|
59
|
+
/** Section the answer came from, e.g. `"§4.1"`. */
|
|
60
|
+
section?: React.ReactNode;
|
|
61
|
+
/** Link to open the document at that section. */
|
|
62
|
+
href?: string;
|
|
63
|
+
}
|
|
64
|
+
interface KnowledgeBaseSourcesProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
65
|
+
/** The documents the answer read. */
|
|
66
|
+
sources: KnowledgeBaseSourceRef[];
|
|
67
|
+
/** The base they came from, for the heading: "Used 3 sources from Sales playbook". */
|
|
68
|
+
baseName?: React.ReactNode;
|
|
69
|
+
/** Heading override. Defaults to `"N sources"` (or the base sentence). */
|
|
70
|
+
heading?: React.ReactNode;
|
|
71
|
+
/** The agent is reading right now: shows "Reading…" and announces it. */
|
|
72
|
+
reading?: boolean;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The source list shown under an answer: exactly which knowledge-base
|
|
76
|
+
* documents (and sections) the agent read, so the answer can be checked.
|
|
77
|
+
* While `reading` it says so — politely announced — and swaps to provenance
|
|
78
|
+
* when the answer finishes.
|
|
79
|
+
*/
|
|
80
|
+
declare const KnowledgeBaseSources: React.ForwardRefExoticComponent<KnowledgeBaseSourcesProps & React.RefAttributes<HTMLDivElement>>;
|
|
81
|
+
interface KnowledgeBaseCoverageProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
82
|
+
/** How well the base covers the question. */
|
|
83
|
+
level: KnowledgeBaseCoverageLevel;
|
|
84
|
+
/** Number of matching passages, e.g. `14`. */
|
|
85
|
+
matches?: number;
|
|
86
|
+
/** Label override. Defaults to "Well-covered", "Partly covered" or "Thin coverage". */
|
|
87
|
+
label?: React.ReactNode;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* A small badge saying how well the knowledge base covers the question —
|
|
91
|
+
* "Well-covered · 14 matches", or a warning when context is thin. The word
|
|
92
|
+
* carries the meaning; colour only reinforces it.
|
|
93
|
+
*/
|
|
94
|
+
declare const KnowledgeBaseCoverage: React.ForwardRefExoticComponent<KnowledgeBaseCoverageProps & React.RefAttributes<HTMLSpanElement>>;
|
|
95
|
+
|
|
96
|
+
export { KnowledgeBase, KnowledgeBaseCoverage, type KnowledgeBaseCoverageLevel, type KnowledgeBaseCoverageProps, type KnowledgeBaseProps, type KnowledgeBaseSourceRef, KnowledgeBaseSources, type KnowledgeBaseSourcesProps, type KnowledgeBaseVariant, type KnowledgeSource, type KnowledgeSourceStatus };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/** Picker layout: a vertical list of rows, or a wrap of chips with document counts. */
|
|
4
|
+
type KnowledgeBaseVariant = "list" | "chips";
|
|
5
|
+
/** Whether a source is usable yet, and whether it is still current. */
|
|
6
|
+
type KnowledgeSourceStatus = "ready" | "processing" | "stale" | "error";
|
|
7
|
+
/** How well the knowledge base covers the question. */
|
|
8
|
+
type KnowledgeBaseCoverageLevel = "high" | "partial" | "low";
|
|
9
|
+
interface KnowledgeSource {
|
|
10
|
+
/** Stable id. */
|
|
11
|
+
id: string;
|
|
12
|
+
/** Source name, e.g. a filename. */
|
|
13
|
+
name: React.ReactNode;
|
|
14
|
+
/** Metadata line, e.g. `"PDF · 12 pages"`. */
|
|
15
|
+
meta?: React.ReactNode;
|
|
16
|
+
/** Leading icon. Defaults to a document glyph. */
|
|
17
|
+
icon?: React.ReactNode;
|
|
18
|
+
/** Readiness, shown as a word on the row. Omit for plain sources. */
|
|
19
|
+
status?: KnowledgeSourceStatus;
|
|
20
|
+
/** Number of documents in the base, shown on chips: `"Product docs · 412"`. */
|
|
21
|
+
count?: number;
|
|
22
|
+
}
|
|
23
|
+
interface KnowledgeBaseProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
|
|
24
|
+
/** The available knowledge sources. */
|
|
25
|
+
sources: KnowledgeSource[];
|
|
26
|
+
/** Selected source ids (controlled). */
|
|
27
|
+
value?: string[];
|
|
28
|
+
/** Initial selection (uncontrolled). */
|
|
29
|
+
defaultValue?: string[];
|
|
30
|
+
/** Called when the selection changes. */
|
|
31
|
+
onValueChange?: (ids: string[]) => void;
|
|
32
|
+
/** Heading above the list. Defaults to `"Knowledge sources"`. */
|
|
33
|
+
heading?: React.ReactNode;
|
|
34
|
+
/** Layout. Defaults to `"list"`; `"chips"` is the compact "Grounded in" picker. */
|
|
35
|
+
variant?: KnowledgeBaseVariant;
|
|
36
|
+
/** Adds an "Add a base" control. */
|
|
37
|
+
onAdd?: () => void;
|
|
38
|
+
/** Label for the add control. Defaults to `"Add a base"`. */
|
|
39
|
+
addLabel?: React.ReactNode;
|
|
40
|
+
/** Recovery for a stale or failed source; renders Refresh / Retry on that row. */
|
|
41
|
+
onRefresh?: (id: string) => void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Lets people choose which knowledge bases the agent grounds in for this
|
|
45
|
+
* session — a list of checkable rows (default) or a compact wrap of chips with
|
|
46
|
+
* document counts (`variant="chips"`). Each source can carry its readiness as a
|
|
47
|
+
* word (Ready, Processing, Needs refresh, Couldn't process) with the recovery
|
|
48
|
+
* on the row that failed. Pair it with KnowledgeBaseSources (what an answer
|
|
49
|
+
* actually read) and KnowledgeBaseCoverage (how well the base covers it).
|
|
50
|
+
* Source: Figma "Context Expansion / Knowledge Bases".
|
|
51
|
+
*/
|
|
52
|
+
declare const KnowledgeBase: React.ForwardRefExoticComponent<KnowledgeBaseProps & React.RefAttributes<HTMLDivElement>>;
|
|
53
|
+
/** One document an answer drew on. */
|
|
54
|
+
interface KnowledgeBaseSourceRef {
|
|
55
|
+
/** Stable id. */
|
|
56
|
+
id: string;
|
|
57
|
+
/** Document title, e.g. `"Renewal Playbook v3"`. */
|
|
58
|
+
name: React.ReactNode;
|
|
59
|
+
/** Section the answer came from, e.g. `"§4.1"`. */
|
|
60
|
+
section?: React.ReactNode;
|
|
61
|
+
/** Link to open the document at that section. */
|
|
62
|
+
href?: string;
|
|
63
|
+
}
|
|
64
|
+
interface KnowledgeBaseSourcesProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
65
|
+
/** The documents the answer read. */
|
|
66
|
+
sources: KnowledgeBaseSourceRef[];
|
|
67
|
+
/** The base they came from, for the heading: "Used 3 sources from Sales playbook". */
|
|
68
|
+
baseName?: React.ReactNode;
|
|
69
|
+
/** Heading override. Defaults to `"N sources"` (or the base sentence). */
|
|
70
|
+
heading?: React.ReactNode;
|
|
71
|
+
/** The agent is reading right now: shows "Reading…" and announces it. */
|
|
72
|
+
reading?: boolean;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The source list shown under an answer: exactly which knowledge-base
|
|
76
|
+
* documents (and sections) the agent read, so the answer can be checked.
|
|
77
|
+
* While `reading` it says so — politely announced — and swaps to provenance
|
|
78
|
+
* when the answer finishes.
|
|
79
|
+
*/
|
|
80
|
+
declare const KnowledgeBaseSources: React.ForwardRefExoticComponent<KnowledgeBaseSourcesProps & React.RefAttributes<HTMLDivElement>>;
|
|
81
|
+
interface KnowledgeBaseCoverageProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
82
|
+
/** How well the base covers the question. */
|
|
83
|
+
level: KnowledgeBaseCoverageLevel;
|
|
84
|
+
/** Number of matching passages, e.g. `14`. */
|
|
85
|
+
matches?: number;
|
|
86
|
+
/** Label override. Defaults to "Well-covered", "Partly covered" or "Thin coverage". */
|
|
87
|
+
label?: React.ReactNode;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* A small badge saying how well the knowledge base covers the question —
|
|
91
|
+
* "Well-covered · 14 matches", or a warning when context is thin. The word
|
|
92
|
+
* carries the meaning; colour only reinforces it.
|
|
93
|
+
*/
|
|
94
|
+
declare const KnowledgeBaseCoverage: React.ForwardRefExoticComponent<KnowledgeBaseCoverageProps & React.RefAttributes<HTMLSpanElement>>;
|
|
95
|
+
|
|
96
|
+
export { KnowledgeBase, KnowledgeBaseCoverage, type KnowledgeBaseCoverageLevel, type KnowledgeBaseCoverageProps, type KnowledgeBaseProps, type KnowledgeBaseSourceRef, KnowledgeBaseSources, type KnowledgeBaseSourcesProps, type KnowledgeBaseVariant, type KnowledgeSource, type KnowledgeSourceStatus };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { cn } from '@nucleux/utils';
|
|
4
|
+
import { Badge } from '@nucleux/badge';
|
|
5
|
+
import { Button } from '@nucleux/button';
|
|
6
|
+
import { Spinner } from '@nucleux/spinner';
|
|
7
|
+
import { Check, Plus, RotateCw, FileText, CircleAlert, AlertTriangle } from 'lucide-react';
|
|
8
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
9
|
+
|
|
10
|
+
// src/index.tsx
|
|
11
|
+
var statusWord = {
|
|
12
|
+
ready: "Ready",
|
|
13
|
+
processing: "Processing",
|
|
14
|
+
stale: "Needs refresh",
|
|
15
|
+
error: "Couldn't process"
|
|
16
|
+
};
|
|
17
|
+
var statusTone = {
|
|
18
|
+
ready: "text-success",
|
|
19
|
+
processing: "text-muted-foreground",
|
|
20
|
+
stale: "text-warning",
|
|
21
|
+
error: "text-destructive"
|
|
22
|
+
};
|
|
23
|
+
var nameText = (n, fallback) => typeof n === "string" ? n : fallback;
|
|
24
|
+
var KnowledgeBase = React.forwardRef(
|
|
25
|
+
({
|
|
26
|
+
sources,
|
|
27
|
+
value,
|
|
28
|
+
defaultValue = [],
|
|
29
|
+
onValueChange,
|
|
30
|
+
heading = "Knowledge sources",
|
|
31
|
+
variant = "list",
|
|
32
|
+
onAdd,
|
|
33
|
+
addLabel = "Add a base",
|
|
34
|
+
onRefresh,
|
|
35
|
+
className,
|
|
36
|
+
...props
|
|
37
|
+
}, ref) => {
|
|
38
|
+
const [internal, setInternal] = React.useState(defaultValue);
|
|
39
|
+
const selected = value !== void 0 ? value : internal;
|
|
40
|
+
const headingId = React.useId();
|
|
41
|
+
const toggle = (id) => {
|
|
42
|
+
const next = selected.includes(id) ? selected.filter((v) => v !== id) : [...selected, id];
|
|
43
|
+
if (value === void 0) setInternal(next);
|
|
44
|
+
onValueChange?.(next);
|
|
45
|
+
};
|
|
46
|
+
if (variant === "chips") {
|
|
47
|
+
return /* @__PURE__ */ jsxs(
|
|
48
|
+
"div",
|
|
49
|
+
{
|
|
50
|
+
ref,
|
|
51
|
+
role: "group",
|
|
52
|
+
"aria-labelledby": heading ? headingId : void 0,
|
|
53
|
+
"aria-label": heading ? void 0 : "Knowledge sources",
|
|
54
|
+
className: cn("flex flex-col gap-2 rounded-xl border border-border bg-background p-3", className),
|
|
55
|
+
...props,
|
|
56
|
+
children: [
|
|
57
|
+
heading && /* @__PURE__ */ jsx("p", { id: headingId, className: "text-xs font-semibold text-foreground", children: heading }),
|
|
58
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1.5", children: [
|
|
59
|
+
sources.map((source) => {
|
|
60
|
+
const active = selected.includes(source.id);
|
|
61
|
+
return /* @__PURE__ */ jsxs(
|
|
62
|
+
"button",
|
|
63
|
+
{
|
|
64
|
+
type: "button",
|
|
65
|
+
role: "checkbox",
|
|
66
|
+
"aria-checked": active,
|
|
67
|
+
onClick: () => toggle(source.id),
|
|
68
|
+
className: cn(
|
|
69
|
+
"inline-flex items-center gap-1 rounded-full border px-2.5 py-1 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-3",
|
|
70
|
+
active ? "border-brand bg-brand-muted/50 text-brand" : "border-border text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
71
|
+
),
|
|
72
|
+
children: [
|
|
73
|
+
active && /* @__PURE__ */ jsx(Check, { "aria-hidden": "true" }),
|
|
74
|
+
source.name,
|
|
75
|
+
source.count != null && /* @__PURE__ */ jsxs("span", { className: "font-normal", children: [
|
|
76
|
+
"\xB7 ",
|
|
77
|
+
source.count.toLocaleString()
|
|
78
|
+
] }),
|
|
79
|
+
source.status && source.status !== "ready" && /* @__PURE__ */ jsxs("span", { className: cn("font-normal", statusTone[source.status]), children: [
|
|
80
|
+
"\xB7 ",
|
|
81
|
+
statusWord[source.status]
|
|
82
|
+
] })
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
source.id
|
|
86
|
+
);
|
|
87
|
+
}),
|
|
88
|
+
onAdd && /* @__PURE__ */ jsxs(
|
|
89
|
+
"button",
|
|
90
|
+
{
|
|
91
|
+
type: "button",
|
|
92
|
+
onClick: onAdd,
|
|
93
|
+
className: "inline-flex items-center gap-1 rounded-full border border-dashed border-border px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-3",
|
|
94
|
+
children: [
|
|
95
|
+
/* @__PURE__ */ jsx(Plus, { "aria-hidden": "true" }),
|
|
96
|
+
addLabel
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
] })
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return /* @__PURE__ */ jsxs(
|
|
106
|
+
"div",
|
|
107
|
+
{
|
|
108
|
+
ref,
|
|
109
|
+
className: cn("flex flex-col gap-2 rounded-xl border border-border bg-background p-3", className),
|
|
110
|
+
...props,
|
|
111
|
+
children: [
|
|
112
|
+
heading && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-1", children: [
|
|
113
|
+
/* @__PURE__ */ jsx("p", { id: headingId, className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: heading }),
|
|
114
|
+
selected.length > 0 && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
115
|
+
selected.length,
|
|
116
|
+
" selected"
|
|
117
|
+
] })
|
|
118
|
+
] }),
|
|
119
|
+
/* @__PURE__ */ jsx(
|
|
120
|
+
"ul",
|
|
121
|
+
{
|
|
122
|
+
"aria-labelledby": heading ? headingId : void 0,
|
|
123
|
+
"aria-label": heading ? void 0 : "Knowledge sources",
|
|
124
|
+
className: "flex flex-col gap-1",
|
|
125
|
+
children: sources.map((source) => {
|
|
126
|
+
const active = selected.includes(source.id);
|
|
127
|
+
const recover = onRefresh && (source.status === "stale" || source.status === "error") ? /* @__PURE__ */ jsx(
|
|
128
|
+
Button,
|
|
129
|
+
{
|
|
130
|
+
variant: "ghost",
|
|
131
|
+
size: "sm",
|
|
132
|
+
onClick: () => onRefresh(source.id),
|
|
133
|
+
"aria-label": `${source.status === "error" ? "Retry" : "Refresh"} ${nameText(source.name, source.id)}`,
|
|
134
|
+
leftIcon: /* @__PURE__ */ jsx(RotateCw, { "aria-hidden": "true", className: "size-3.5" }),
|
|
135
|
+
className: "h-7 shrink-0 px-2 text-xs",
|
|
136
|
+
children: source.status === "error" ? "Retry" : "Refresh"
|
|
137
|
+
}
|
|
138
|
+
) : null;
|
|
139
|
+
return /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-1", "data-status": source.status, children: [
|
|
140
|
+
/* @__PURE__ */ jsxs(
|
|
141
|
+
"button",
|
|
142
|
+
{
|
|
143
|
+
type: "button",
|
|
144
|
+
role: "checkbox",
|
|
145
|
+
"aria-checked": active,
|
|
146
|
+
onClick: () => toggle(source.id),
|
|
147
|
+
className: cn(
|
|
148
|
+
"flex w-full min-w-0 flex-1 items-center gap-3 rounded-lg border px-3 py-2 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
149
|
+
active ? "border-brand bg-brand-muted/40" : "border-transparent hover:bg-accent"
|
|
150
|
+
),
|
|
151
|
+
children: [
|
|
152
|
+
/* @__PURE__ */ jsx("span", { className: "inline-flex size-8 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground [&_svg]:size-4", children: source.icon ?? /* @__PURE__ */ jsx(FileText, { "aria-hidden": "true" }) }),
|
|
153
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
154
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-foreground", children: source.name }),
|
|
155
|
+
(source.meta || source.status || source.count != null) && /* @__PURE__ */ jsxs("span", { className: "truncate text-xs text-muted-foreground", children: [
|
|
156
|
+
source.status && /* @__PURE__ */ jsx("span", { className: cn("font-medium", statusTone[source.status]), children: statusWord[source.status] }),
|
|
157
|
+
source.status && (source.meta || source.count != null) && " \xB7 ",
|
|
158
|
+
source.count != null && `${source.count.toLocaleString()} docs`,
|
|
159
|
+
source.count != null && source.meta && " \xB7 ",
|
|
160
|
+
source.meta
|
|
161
|
+
] })
|
|
162
|
+
] }),
|
|
163
|
+
/* @__PURE__ */ jsx(
|
|
164
|
+
"span",
|
|
165
|
+
{
|
|
166
|
+
className: cn(
|
|
167
|
+
"inline-flex size-5 shrink-0 items-center justify-center rounded-md border transition-colors [&_svg]:size-3.5",
|
|
168
|
+
active ? "border-brand bg-brand text-brand-foreground" : "border-border"
|
|
169
|
+
),
|
|
170
|
+
children: active && /* @__PURE__ */ jsx(Check, { "aria-hidden": "true" })
|
|
171
|
+
}
|
|
172
|
+
)
|
|
173
|
+
]
|
|
174
|
+
}
|
|
175
|
+
),
|
|
176
|
+
recover
|
|
177
|
+
] }, source.id);
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
),
|
|
181
|
+
onAdd && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: onAdd, leftIcon: /* @__PURE__ */ jsx(Plus, { "aria-hidden": "true", className: "size-4" }), className: "self-start", children: addLabel })
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
KnowledgeBase.displayName = "KnowledgeBase";
|
|
188
|
+
var KnowledgeBaseSources = React.forwardRef(
|
|
189
|
+
({ sources, baseName, heading, reading = false, className, ...props }, ref) => {
|
|
190
|
+
const headingId = React.useId();
|
|
191
|
+
const n = sources.length;
|
|
192
|
+
const count = `${n} ${n === 1 ? "source" : "sources"}`;
|
|
193
|
+
const title = heading ?? (reading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
194
|
+
"Reading",
|
|
195
|
+
baseName ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
196
|
+
" ",
|
|
197
|
+
baseName
|
|
198
|
+
] }) : null,
|
|
199
|
+
"\u2026"
|
|
200
|
+
] }) : baseName ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
201
|
+
"Used ",
|
|
202
|
+
count,
|
|
203
|
+
" from ",
|
|
204
|
+
baseName
|
|
205
|
+
] }) : count);
|
|
206
|
+
return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex flex-col gap-1.5", className), ...props, children: [
|
|
207
|
+
/* @__PURE__ */ jsxs(
|
|
208
|
+
"p",
|
|
209
|
+
{
|
|
210
|
+
id: headingId,
|
|
211
|
+
role: "status",
|
|
212
|
+
"aria-live": "polite",
|
|
213
|
+
className: "inline-flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground",
|
|
214
|
+
children: [
|
|
215
|
+
reading && /* @__PURE__ */ jsx(Spinner, { "aria-hidden": "true", role: void 0, className: "size-3 motion-reduce:animate-none" }),
|
|
216
|
+
title
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
),
|
|
220
|
+
n > 0 && /* @__PURE__ */ jsx("ul", { "aria-labelledby": headingId, className: "flex flex-col gap-1", children: sources.map((s) => {
|
|
221
|
+
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
222
|
+
/* @__PURE__ */ jsx(FileText, { "aria-hidden": "true", className: "size-3.5 shrink-0 text-muted-foreground" }),
|
|
223
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: s.name }),
|
|
224
|
+
s.section && /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-muted-foreground", children: [
|
|
225
|
+
"\xB7 ",
|
|
226
|
+
s.section
|
|
227
|
+
] })
|
|
228
|
+
] });
|
|
229
|
+
const cls = "flex items-center gap-2 rounded-md bg-muted/60 px-2.5 py-1.5 text-xs text-foreground";
|
|
230
|
+
return /* @__PURE__ */ jsx("li", { children: s.href ? /* @__PURE__ */ jsx(
|
|
231
|
+
"a",
|
|
232
|
+
{
|
|
233
|
+
href: s.href,
|
|
234
|
+
className: cn(cls, "transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"),
|
|
235
|
+
children: body
|
|
236
|
+
}
|
|
237
|
+
) : /* @__PURE__ */ jsx("span", { className: cls, children: body }) }, s.id);
|
|
238
|
+
}) })
|
|
239
|
+
] });
|
|
240
|
+
}
|
|
241
|
+
);
|
|
242
|
+
KnowledgeBaseSources.displayName = "KnowledgeBaseSources";
|
|
243
|
+
var coverage = {
|
|
244
|
+
high: { word: "Well-covered", icon: /* @__PURE__ */ jsx(Check, { "aria-hidden": "true", className: "size-3" }), cls: "bg-success/15 text-success" },
|
|
245
|
+
partial: { word: "Partly covered", icon: /* @__PURE__ */ jsx(AlertTriangle, { "aria-hidden": "true", className: "size-3" }), cls: "bg-warning/15 text-warning" },
|
|
246
|
+
low: { word: "Thin coverage", icon: /* @__PURE__ */ jsx(CircleAlert, { "aria-hidden": "true", className: "size-3" }), cls: "bg-destructive/15 text-destructive" }
|
|
247
|
+
};
|
|
248
|
+
var KnowledgeBaseCoverage = React.forwardRef(
|
|
249
|
+
({ level, matches, label, className, ...props }, ref) => {
|
|
250
|
+
const c = coverage[level];
|
|
251
|
+
return /* @__PURE__ */ jsxs(Badge, { ref, variant: "outline", icon: c.icon, "data-level": level, className: cn("border-transparent", c.cls, className), ...props, children: [
|
|
252
|
+
label ?? c.word,
|
|
253
|
+
matches != null && /* @__PURE__ */ jsxs("span", { className: "font-normal", children: [
|
|
254
|
+
"\xB7 ",
|
|
255
|
+
matches,
|
|
256
|
+
" ",
|
|
257
|
+
matches === 1 ? "match" : "matches"
|
|
258
|
+
] })
|
|
259
|
+
] });
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
KnowledgeBaseCoverage.displayName = "KnowledgeBaseCoverage";
|
|
263
|
+
|
|
264
|
+
export { KnowledgeBase, KnowledgeBaseCoverage, KnowledgeBaseSources };
|
|
265
|
+
//# sourceMappingURL=index.js.map
|
|
266
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@nucleux/utils\";\nimport { Badge } from \"@nucleux/badge\";\nimport { Button } from \"@nucleux/button\";\nimport { Spinner } from \"@nucleux/spinner\";\nimport { AlertTriangle, Check, CircleAlert, FileText, Plus, RotateCw } from \"lucide-react\";\n\n/** Picker layout: a vertical list of rows, or a wrap of chips with document counts. */\nexport type KnowledgeBaseVariant = \"list\" | \"chips\";\n\n/** Whether a source is usable yet, and whether it is still current. */\nexport type KnowledgeSourceStatus = \"ready\" | \"processing\" | \"stale\" | \"error\";\n\n/** How well the knowledge base covers the question. */\nexport type KnowledgeBaseCoverageLevel = \"high\" | \"partial\" | \"low\";\n\nexport interface KnowledgeSource {\n /** Stable id. */\n id: string;\n /** Source name, e.g. a filename. */\n name: React.ReactNode;\n /** Metadata line, e.g. `\"PDF · 12 pages\"`. */\n meta?: React.ReactNode;\n /** Leading icon. Defaults to a document glyph. */\n icon?: React.ReactNode;\n /** Readiness, shown as a word on the row. Omit for plain sources. */\n status?: KnowledgeSourceStatus;\n /** Number of documents in the base, shown on chips: `\"Product docs · 412\"`. */\n count?: number;\n}\n\nexport interface KnowledgeBaseProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n /** The available knowledge sources. */\n sources: KnowledgeSource[];\n /** Selected source ids (controlled). */\n value?: string[];\n /** Initial selection (uncontrolled). */\n defaultValue?: string[];\n /** Called when the selection changes. */\n onValueChange?: (ids: string[]) => void;\n /** Heading above the list. Defaults to `\"Knowledge sources\"`. */\n heading?: React.ReactNode;\n /** Layout. Defaults to `\"list\"`; `\"chips\"` is the compact \"Grounded in\" picker. */\n variant?: KnowledgeBaseVariant;\n /** Adds an \"Add a base\" control. */\n onAdd?: () => void;\n /** Label for the add control. Defaults to `\"Add a base\"`. */\n addLabel?: React.ReactNode;\n /** Recovery for a stale or failed source; renders Refresh / Retry on that row. */\n onRefresh?: (id: string) => void;\n}\n\nconst statusWord: Record<KnowledgeSourceStatus, string> = {\n ready: \"Ready\",\n processing: \"Processing\",\n stale: \"Needs refresh\",\n error: \"Couldn't process\",\n};\n\nconst statusTone: Record<KnowledgeSourceStatus, string> = {\n ready: \"text-success\",\n processing: \"text-muted-foreground\",\n stale: \"text-warning\",\n error: \"text-destructive\",\n};\n\nconst nameText = (n: React.ReactNode, fallback: string) => (typeof n === \"string\" ? n : fallback);\n\n/**\n * Lets people choose which knowledge bases the agent grounds in for this\n * session — a list of checkable rows (default) or a compact wrap of chips with\n * document counts (`variant=\"chips\"`). Each source can carry its readiness as a\n * word (Ready, Processing, Needs refresh, Couldn't process) with the recovery\n * on the row that failed. Pair it with KnowledgeBaseSources (what an answer\n * actually read) and KnowledgeBaseCoverage (how well the base covers it).\n * Source: Figma \"Context Expansion / Knowledge Bases\".\n */\nexport const KnowledgeBase = React.forwardRef<HTMLDivElement, KnowledgeBaseProps>(\n (\n {\n sources,\n value,\n defaultValue = [],\n onValueChange,\n heading = \"Knowledge sources\",\n variant = \"list\",\n onAdd,\n addLabel = \"Add a base\",\n onRefresh,\n className,\n ...props\n },\n ref,\n ) => {\n const [internal, setInternal] = React.useState<string[]>(defaultValue);\n const selected = value !== undefined ? value : internal;\n const headingId = React.useId();\n const toggle = (id: string) => {\n const next = selected.includes(id) ? selected.filter((v) => v !== id) : [...selected, id];\n if (value === undefined) setInternal(next);\n onValueChange?.(next);\n };\n\n if (variant === \"chips\") {\n return (\n <div\n ref={ref}\n role=\"group\"\n aria-labelledby={heading ? headingId : undefined}\n aria-label={heading ? undefined : \"Knowledge sources\"}\n className={cn(\"flex flex-col gap-2 rounded-xl border border-border bg-background p-3\", className)}\n {...props}\n >\n {heading && (\n <p id={headingId} className=\"text-xs font-semibold text-foreground\">\n {heading}\n </p>\n )}\n <div className=\"flex flex-wrap gap-1.5\">\n {sources.map((source) => {\n const active = selected.includes(source.id);\n return (\n <button\n key={source.id}\n type=\"button\"\n role=\"checkbox\"\n aria-checked={active}\n onClick={() => toggle(source.id)}\n className={cn(\n \"inline-flex items-center gap-1 rounded-full border px-2.5 py-1 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-3\",\n active\n ? \"border-brand bg-brand-muted/50 text-brand\"\n : \"border-border text-muted-foreground hover:bg-accent hover:text-foreground\",\n )}\n >\n {active && <Check aria-hidden=\"true\" />}\n {source.name}\n {source.count != null && <span className=\"font-normal\">· {source.count.toLocaleString()}</span>}\n {source.status && source.status !== \"ready\" && (\n <span className={cn(\"font-normal\", statusTone[source.status])}>· {statusWord[source.status]}</span>\n )}\n </button>\n );\n })}\n {onAdd && (\n <button\n type=\"button\"\n onClick={onAdd}\n className=\"inline-flex items-center gap-1 rounded-full border border-dashed border-border px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-3\"\n >\n <Plus aria-hidden=\"true\" />\n {addLabel}\n </button>\n )}\n </div>\n </div>\n );\n }\n\n return (\n <div\n ref={ref}\n className={cn(\"flex flex-col gap-2 rounded-xl border border-border bg-background p-3\", className)}\n {...props}\n >\n {heading && (\n <div className=\"flex items-center justify-between px-1\">\n <p id={headingId} className=\"text-xs font-medium uppercase tracking-wide text-muted-foreground\">\n {heading}\n </p>\n {selected.length > 0 && (\n <span className=\"text-xs text-muted-foreground\">{selected.length} selected</span>\n )}\n </div>\n )}\n <ul\n aria-labelledby={heading ? headingId : undefined}\n aria-label={heading ? undefined : \"Knowledge sources\"}\n className=\"flex flex-col gap-1\"\n >\n {sources.map((source) => {\n const active = selected.includes(source.id);\n const recover =\n onRefresh && (source.status === \"stale\" || source.status === \"error\") ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => onRefresh(source.id)}\n aria-label={`${source.status === \"error\" ? \"Retry\" : \"Refresh\"} ${nameText(source.name, source.id)}`}\n leftIcon={<RotateCw aria-hidden=\"true\" className=\"size-3.5\" />}\n className=\"h-7 shrink-0 px-2 text-xs\"\n >\n {source.status === \"error\" ? \"Retry\" : \"Refresh\"}\n </Button>\n ) : null;\n return (\n <li key={source.id} className=\"flex items-center gap-1\" data-status={source.status}>\n <button\n type=\"button\"\n role=\"checkbox\"\n aria-checked={active}\n onClick={() => toggle(source.id)}\n className={cn(\n \"flex w-full min-w-0 flex-1 items-center gap-3 rounded-lg border px-3 py-2 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n active ? \"border-brand bg-brand-muted/40\" : \"border-transparent hover:bg-accent\",\n )}\n >\n <span className=\"inline-flex size-8 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground [&_svg]:size-4\">\n {source.icon ?? <FileText aria-hidden=\"true\" />}\n </span>\n <span className=\"flex min-w-0 flex-1 flex-col\">\n <span className=\"truncate text-sm font-medium text-foreground\">{source.name}</span>\n {(source.meta || source.status || source.count != null) && (\n <span className=\"truncate text-xs text-muted-foreground\">\n {source.status && (\n <span className={cn(\"font-medium\", statusTone[source.status])}>\n {statusWord[source.status]}\n </span>\n )}\n {source.status && (source.meta || source.count != null) && \" · \"}\n {source.count != null && `${source.count.toLocaleString()} docs`}\n {source.count != null && source.meta && \" · \"}\n {source.meta}\n </span>\n )}\n </span>\n <span\n className={cn(\n \"inline-flex size-5 shrink-0 items-center justify-center rounded-md border transition-colors [&_svg]:size-3.5\",\n active ? \"border-brand bg-brand text-brand-foreground\" : \"border-border\",\n )}\n >\n {active && <Check aria-hidden=\"true\" />}\n </span>\n </button>\n {recover}\n </li>\n );\n })}\n </ul>\n {onAdd && (\n <Button variant=\"ghost\" size=\"sm\" onClick={onAdd} leftIcon={<Plus aria-hidden=\"true\" className=\"size-4\" />} className=\"self-start\">\n {addLabel}\n </Button>\n )}\n </div>\n );\n },\n);\nKnowledgeBase.displayName = \"KnowledgeBase\";\n\n/** One document an answer drew on. */\nexport interface KnowledgeBaseSourceRef {\n /** Stable id. */\n id: string;\n /** Document title, e.g. `\"Renewal Playbook v3\"`. */\n name: React.ReactNode;\n /** Section the answer came from, e.g. `\"§4.1\"`. */\n section?: React.ReactNode;\n /** Link to open the document at that section. */\n href?: string;\n}\n\nexport interface KnowledgeBaseSourcesProps extends React.HTMLAttributes<HTMLDivElement> {\n /** The documents the answer read. */\n sources: KnowledgeBaseSourceRef[];\n /** The base they came from, for the heading: \"Used 3 sources from Sales playbook\". */\n baseName?: React.ReactNode;\n /** Heading override. Defaults to `\"N sources\"` (or the base sentence). */\n heading?: React.ReactNode;\n /** The agent is reading right now: shows \"Reading…\" and announces it. */\n reading?: boolean;\n}\n\n/**\n * The source list shown under an answer: exactly which knowledge-base\n * documents (and sections) the agent read, so the answer can be checked.\n * While `reading` it says so — politely announced — and swaps to provenance\n * when the answer finishes.\n */\nexport const KnowledgeBaseSources = React.forwardRef<HTMLDivElement, KnowledgeBaseSourcesProps>(\n ({ sources, baseName, heading, reading = false, className, ...props }, ref) => {\n const headingId = React.useId();\n const n = sources.length;\n const count = `${n} ${n === 1 ? \"source\" : \"sources\"}`;\n const title =\n heading ??\n (reading ? (\n <>Reading{baseName ? <> {baseName}</> : null}…</>\n ) : baseName ? (\n <>\n Used {count} from {baseName}\n </>\n ) : (\n count\n ));\n return (\n <div ref={ref} className={cn(\"flex flex-col gap-1.5\", className)} {...props}>\n <p\n id={headingId}\n role=\"status\"\n aria-live=\"polite\"\n className=\"inline-flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground\"\n >\n {reading && <Spinner aria-hidden=\"true\" role={undefined} className=\"size-3 motion-reduce:animate-none\" />}\n {title}\n </p>\n {n > 0 && (\n <ul aria-labelledby={headingId} className=\"flex flex-col gap-1\">\n {sources.map((s) => {\n const body = (\n <>\n <FileText aria-hidden=\"true\" className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{s.name}</span>\n {s.section && <span className=\"shrink-0 text-muted-foreground\">· {s.section}</span>}\n </>\n );\n const cls =\n \"flex items-center gap-2 rounded-md bg-muted/60 px-2.5 py-1.5 text-xs text-foreground\";\n return (\n <li key={s.id}>\n {s.href ? (\n <a\n href={s.href}\n className={cn(cls, \"transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\")}\n >\n {body}\n </a>\n ) : (\n <span className={cls}>{body}</span>\n )}\n </li>\n );\n })}\n </ul>\n )}\n </div>\n );\n },\n);\nKnowledgeBaseSources.displayName = \"KnowledgeBaseSources\";\n\nexport interface KnowledgeBaseCoverageProps extends React.HTMLAttributes<HTMLSpanElement> {\n /** How well the base covers the question. */\n level: KnowledgeBaseCoverageLevel;\n /** Number of matching passages, e.g. `14`. */\n matches?: number;\n /** Label override. Defaults to \"Well-covered\", \"Partly covered\" or \"Thin coverage\". */\n label?: React.ReactNode;\n}\n\nconst coverage: Record<KnowledgeBaseCoverageLevel, { word: string; icon: React.ReactNode; cls: string }> = {\n high: { word: \"Well-covered\", icon: <Check aria-hidden=\"true\" className=\"size-3\" />, cls: \"bg-success/15 text-success\" },\n partial: { word: \"Partly covered\", icon: <AlertTriangle aria-hidden=\"true\" className=\"size-3\" />, cls: \"bg-warning/15 text-warning\" },\n low: { word: \"Thin coverage\", icon: <CircleAlert aria-hidden=\"true\" className=\"size-3\" />, cls: \"bg-destructive/15 text-destructive\" },\n};\n\n/**\n * A small badge saying how well the knowledge base covers the question —\n * \"Well-covered · 14 matches\", or a warning when context is thin. The word\n * carries the meaning; colour only reinforces it.\n */\nexport const KnowledgeBaseCoverage = React.forwardRef<HTMLSpanElement, KnowledgeBaseCoverageProps>(\n ({ level, matches, label, className, ...props }, ref) => {\n const c = coverage[level];\n return (\n <Badge ref={ref} variant=\"outline\" icon={c.icon} data-level={level} className={cn(\"border-transparent\", c.cls, className)} {...props}>\n {label ?? c.word}\n {matches != null && <span className=\"font-normal\">· {matches} {matches === 1 ? \"match\" : \"matches\"}</span>}\n </Badge>\n );\n },\n);\nKnowledgeBaseCoverage.displayName = \"KnowledgeBaseCoverage\";\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,61 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nucleux/knowledge-base",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.7.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"description": "Knowledge Base — choose the sources the agent may draw on — Nucleux agentic UI for React.",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"nucleux",
|
|
9
|
+
"react",
|
|
10
|
+
"ui",
|
|
11
|
+
"components",
|
|
12
|
+
"agentic",
|
|
13
|
+
"ai",
|
|
14
|
+
"tailwind",
|
|
15
|
+
"knowledge-base"
|
|
16
|
+
],
|
|
17
|
+
"main": "./dist/index.cjs",
|
|
18
|
+
"module": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js",
|
|
24
|
+
"require": "./dist/index.cjs"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"react": ">=18",
|
|
33
|
+
"react-dom": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@nucleux/utils": "0.7.0",
|
|
37
|
+
"@nucleux/badge": "0.7.0",
|
|
38
|
+
"@nucleux/button": "0.7.0",
|
|
39
|
+
"@nucleux/spinner": "0.7.0",
|
|
40
|
+
"lucide-react": "^0.469.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"tsup": "^8.3.5",
|
|
44
|
+
"typescript": "^5.7.2",
|
|
45
|
+
"@types/react": "^18.3.18",
|
|
46
|
+
"react": "^18.3.1"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/intelligaia-open-labs/nucleux.git",
|
|
55
|
+
"directory": "packages/knowledge-base"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/intelligaia-open-labs/nucleux#readme",
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "tsup"
|
|
60
|
+
}
|
|
6
61
|
}
|