@brainfish-ai/components 0.21.1 → 0.22.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/dist/esm/chunks/{layout.zKOfkHvJ.js → layout.DoGej6-0.js} +2 -2
- package/dist/esm/chunks/{layout.zKOfkHvJ.js.map → layout.DoGej6-0.js.map} +1 -1
- package/dist/esm/chunks/{review-list.jCfkezEr.js → review-list.DYyAYTEY.js} +25 -8
- package/dist/esm/chunks/review-list.DYyAYTEY.js.map +1 -0
- package/dist/esm/chunks/{sidebar.TmitnaWX.js → sidebar.CXE4WUtM.js} +41 -14
- package/dist/esm/chunks/sidebar.CXE4WUtM.js.map +1 -0
- package/dist/esm/components/ui/button.js +2 -2
- package/dist/esm/components/ui/button.js.map +1 -1
- package/dist/esm/global.css +1 -1
- package/dist/esm/index.js +3 -3
- package/dist/esm/knowledge-review.css +1 -0
- package/dist/esm/layouts/full-layout.js +1 -1
- package/dist/esm/layouts/sidebar.js +1 -1
- package/dist/esm/scenes/knowledge-review.js +252 -0
- package/dist/esm/scenes/knowledge-review.js.map +1 -0
- package/dist/full-layout.d.ts +6 -1
- package/dist/index.d.ts +22 -3
- package/dist/knowledge-review.d.ts +91 -0
- package/dist/scenes/knowledge-review.d.ts +2 -0
- package/dist/sidebar.d.ts +6 -1
- package/dist/stats.html +1 -1
- package/package.json +2 -1
- package/dist/esm/chunks/review-list.jCfkezEr.js.map +0 -1
- package/dist/esm/chunks/sidebar.TmitnaWX.js.map +0 -1
- package/dist/esm/scenes/knowledge-review/review-list.js +0 -2
- package/dist/esm/scenes/knowledge-review/review-list.js.map +0 -1
- package/dist/review-list.d.ts +0 -47
- package/dist/scenes/knowledge-review/review-list.d.ts +0 -2
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import React__default, { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { u as useReviewsSelection, E as EditCount, b as ReviewsSelectionProvider, a as ReviewList } from '../chunks/review-list.DYyAYTEY.js';
|
|
4
|
+
import parse from 'html-react-parser';
|
|
5
|
+
import { Card, CardHeader, CardTitle, CardContent } from '../components/ui/card.js';
|
|
6
|
+
import { ScrollArea } from '../components/ui/scroll-area.js';
|
|
7
|
+
import { CaretDown, CaretUp, Backspace, CheckCircle } from '@phosphor-icons/react';
|
|
8
|
+
import { Button } from '../components/ui/button.js';
|
|
9
|
+
import { Tooltip, TooltipTrigger, TooltipContent } from '../components/ui/tooltip.js';
|
|
10
|
+
import { ButtonGroup } from '../components/ui/button-group.js';
|
|
11
|
+
import { c as cn } from '../chunks/utils.Cwtlq8dh.js';
|
|
12
|
+
import { d as dark } from '../chunks/dark.Cl9Z44CU.js';
|
|
13
|
+
import { S as StatusBadge } from '../chunks/status-badge.DkPNh30S.js';
|
|
14
|
+
|
|
15
|
+
import '../knowledge-review.css';function KnowledgeReviewDiffLoader({ fetchDiffForItem }) {
|
|
16
|
+
const { selectedItem, setLoadedDiffDoc, setLoadingDiff } = useReviewsSelection();
|
|
17
|
+
const loadDiffForItem = useCallback(
|
|
18
|
+
async (itemId, signal) => {
|
|
19
|
+
setLoadingDiff(true);
|
|
20
|
+
setLoadedDiffDoc(null);
|
|
21
|
+
try {
|
|
22
|
+
const doc = await fetchDiffForItem(itemId);
|
|
23
|
+
if (signal.aborted) return;
|
|
24
|
+
setLoadedDiffDoc(doc);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
if (signal.aborted) return;
|
|
27
|
+
console.error("Failed to fetch diff:", error);
|
|
28
|
+
setLoadedDiffDoc(null);
|
|
29
|
+
} finally {
|
|
30
|
+
if (!signal.aborted) {
|
|
31
|
+
setLoadingDiff(false);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
[fetchDiffForItem, setLoadedDiffDoc, setLoadingDiff]
|
|
36
|
+
);
|
|
37
|
+
const selectedId = selectedItem?.id;
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!selectedId) {
|
|
40
|
+
setLoadedDiffDoc(null);
|
|
41
|
+
setLoadingDiff(false);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const abortController = new AbortController();
|
|
45
|
+
void loadDiffForItem(selectedId, abortController.signal);
|
|
46
|
+
return () => {
|
|
47
|
+
abortController.abort();
|
|
48
|
+
};
|
|
49
|
+
}, [selectedId, loadDiffForItem]);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const SuggestionsHeader = React.forwardRef(function SuggestionsHeader2({
|
|
54
|
+
title,
|
|
55
|
+
isNew,
|
|
56
|
+
suggestionsCount,
|
|
57
|
+
sourceIcon,
|
|
58
|
+
sourceDescription,
|
|
59
|
+
currentArticleIndex,
|
|
60
|
+
totalArticles,
|
|
61
|
+
onNextArticle,
|
|
62
|
+
onPreviousArticle,
|
|
63
|
+
onRejectAllSuggestions,
|
|
64
|
+
onAcceptAllSuggestions,
|
|
65
|
+
className,
|
|
66
|
+
...props
|
|
67
|
+
}, ref) {
|
|
68
|
+
const hasNextArticle = totalArticles > 0 && currentArticleIndex < totalArticles - 1;
|
|
69
|
+
const hasPreviousArticle = currentArticleIndex > 0;
|
|
70
|
+
const currentArticle = totalArticles === 0 ? 0 : currentArticleIndex + 1;
|
|
71
|
+
const hasArticles = totalArticles > 0;
|
|
72
|
+
let status;
|
|
73
|
+
if (isNew) {
|
|
74
|
+
status = /* @__PURE__ */ React.createElement(StatusBadge, { variant: "success" }, "New");
|
|
75
|
+
} else if (suggestionsCount) {
|
|
76
|
+
status = /* @__PURE__ */ React.createElement(EditCount, { count: suggestionsCount });
|
|
77
|
+
}
|
|
78
|
+
return /* @__PURE__ */ React.createElement("div", { ref, className: cn("flex flex-col gap-4", className), ...props }, /* @__PURE__ */ React.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React.createElement("h1", { className: "heading-lg" }, title), /* @__PURE__ */ React.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React.createElement("p", { className: "text-xs text-foreground" }, /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, hasArticles ? `${currentArticle}/${totalArticles}` : "0"), /* @__PURE__ */ React.createElement("span", { className: "sr-only", "aria-live": "polite" }, hasArticles ? `Article ${currentArticle} of ${totalArticles}` : "No articles")), /* @__PURE__ */ React.createElement(Tooltip, null, /* @__PURE__ */ React.createElement(TooltipTrigger, { asChild: true }, /* @__PURE__ */ React.createElement(
|
|
79
|
+
Button,
|
|
80
|
+
{
|
|
81
|
+
variant: "ghost",
|
|
82
|
+
size: "icon",
|
|
83
|
+
disabled: !hasNextArticle,
|
|
84
|
+
onClick: onNextArticle,
|
|
85
|
+
"aria-label": "Go to next article"
|
|
86
|
+
},
|
|
87
|
+
/* @__PURE__ */ React.createElement(CaretDown, { "aria-hidden": "true" })
|
|
88
|
+
)), /* @__PURE__ */ React.createElement(TooltipContent, { side: "bottom", borderColor: "transparent", background: dark[900], textColor: dark[100] }, "Next Article")), /* @__PURE__ */ React.createElement(Tooltip, null, /* @__PURE__ */ React.createElement(TooltipTrigger, { asChild: true }, /* @__PURE__ */ React.createElement(
|
|
89
|
+
Button,
|
|
90
|
+
{
|
|
91
|
+
variant: "ghost",
|
|
92
|
+
size: "icon",
|
|
93
|
+
disabled: !hasPreviousArticle,
|
|
94
|
+
onClick: onPreviousArticle,
|
|
95
|
+
"aria-label": "Go to previous article"
|
|
96
|
+
},
|
|
97
|
+
/* @__PURE__ */ React.createElement(CaretUp, { "aria-hidden": "true" })
|
|
98
|
+
)), /* @__PURE__ */ React.createElement(TooltipContent, { side: "top", borderColor: "transparent", background: dark[900], textColor: dark[100] }, "Previous Article")))), /* @__PURE__ */ React.createElement("div", { className: "flex-1 px-2 pb-2 flex justify-between items-center" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2" }, status, sourceIcon && /* @__PURE__ */ React.createElement("span", { className: "shrink-0 flex items-center" }, sourceIcon), sourceDescription && /* @__PURE__ */ React.createElement("span", { className: "text-xs text-subtle" }, sourceDescription)), /* @__PURE__ */ React.createElement(ButtonGroup, { orientation: "horizontal", rounded: false, "aria-label": "Suggestion actions", className: "gap-1" }, /* @__PURE__ */ React.createElement(Button, { variant: "ghost", onClick: onRejectAllSuggestions }, /* @__PURE__ */ React.createElement(Backspace, { "aria-hidden": "true" }), "Reject"), /* @__PURE__ */ React.createElement(Button, { variant: "shadow", onClick: onAcceptAllSuggestions }, /* @__PURE__ */ React.createElement(CheckCircle, { "aria-hidden": "true" }), "Accept"))));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
function countSuggestionsInRoot(root) {
|
|
102
|
+
const adjacentPairs = root.querySelectorAll("del + ins");
|
|
103
|
+
const standaloneIns = root.querySelectorAll("ins:not(del + ins)");
|
|
104
|
+
const allDel = root.querySelectorAll("del");
|
|
105
|
+
const standaloneDel = Array.from(allDel).filter((del) => del.nextElementSibling?.tagName !== "INS");
|
|
106
|
+
return adjacentPairs.length + standaloneIns.length + standaloneDel.length;
|
|
107
|
+
}
|
|
108
|
+
const countSuggestions = (htmlString) => {
|
|
109
|
+
if (!htmlString || typeof DOMParser === "undefined") {
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
const parser = new DOMParser();
|
|
113
|
+
const doc = parser.parseFromString(htmlString, "text/html");
|
|
114
|
+
return countSuggestionsInRoot(doc);
|
|
115
|
+
};
|
|
116
|
+
const countSuggestionsInElement = (element) => {
|
|
117
|
+
if (!element) return 0;
|
|
118
|
+
return countSuggestionsInRoot(element);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
function KnowledgeReviewPanel({
|
|
122
|
+
diffTitle,
|
|
123
|
+
items,
|
|
124
|
+
children,
|
|
125
|
+
sourceIcon,
|
|
126
|
+
sourceDescription
|
|
127
|
+
}) {
|
|
128
|
+
const { selectedItem, setSelectedItem, loadedDiffDoc, loadingDiff, onApproveAllSuggestions, onRejectAllSuggestions } = useReviewsSelection();
|
|
129
|
+
const title = loadingDiff ? "" : parse(loadedDiffDoc?.diffTitle ?? diffTitle);
|
|
130
|
+
const body = loadingDiff ? /* @__PURE__ */ React__default.createElement("div", { className: "text-muted-foreground" }, "Loading diff…") : loadedDiffDoc?.diffContent;
|
|
131
|
+
const titleSuggestionsCount = loadingDiff ? 0 : countSuggestions(loadedDiffDoc?.diffTitle ?? diffTitle);
|
|
132
|
+
const bodyRef = useRef(null);
|
|
133
|
+
const [bodySuggestionsCount, setBodySuggestionsCount] = useState(0);
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
if (loadingDiff || !loadedDiffDoc) {
|
|
136
|
+
setBodySuggestionsCount(0);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const el = bodyRef.current;
|
|
140
|
+
if (!el) return;
|
|
141
|
+
const id = requestAnimationFrame(() => {
|
|
142
|
+
setBodySuggestionsCount(countSuggestionsInElement(el));
|
|
143
|
+
});
|
|
144
|
+
return () => cancelAnimationFrame(id);
|
|
145
|
+
}, [loadingDiff, loadedDiffDoc]);
|
|
146
|
+
const totalSuggestionsCount = titleSuggestionsCount + bodySuggestionsCount;
|
|
147
|
+
const totalArticles = items.length;
|
|
148
|
+
const currentArticleIndex = selectedItem ? Math.max(
|
|
149
|
+
0,
|
|
150
|
+
items.findIndex((i) => i.id === selectedItem.id)
|
|
151
|
+
) : 0;
|
|
152
|
+
const onNextArticle = useCallback(() => {
|
|
153
|
+
if (currentArticleIndex < totalArticles - 1) {
|
|
154
|
+
setSelectedItem(items[currentArticleIndex + 1] ?? null);
|
|
155
|
+
}
|
|
156
|
+
}, [currentArticleIndex, items, setSelectedItem, totalArticles]);
|
|
157
|
+
const onPreviousArticle = useCallback(() => {
|
|
158
|
+
if (currentArticleIndex > 0) {
|
|
159
|
+
setSelectedItem(items[currentArticleIndex - 1] ?? null);
|
|
160
|
+
}
|
|
161
|
+
}, [currentArticleIndex, items, setSelectedItem]);
|
|
162
|
+
return /* @__PURE__ */ React__default.createElement(Card, { className: "py-6 pl-4", "data-name": "knowledge-reviews" }, /* @__PURE__ */ React__default.createElement(ScrollArea, { className: "h-[calc(var(--knowledge-review-scroll-height)-50px)] pr-8" }, /* @__PURE__ */ React__default.createElement(CardHeader, { className: "p-0" }, /* @__PURE__ */ React__default.createElement(CardTitle, { className: "border-b border-dark-300 mb-6 pb-2" }, /* @__PURE__ */ React__default.createElement(
|
|
163
|
+
SuggestionsHeader,
|
|
164
|
+
{
|
|
165
|
+
title,
|
|
166
|
+
isNew: loadedDiffDoc?.isNew ?? false,
|
|
167
|
+
suggestionsCount: totalSuggestionsCount,
|
|
168
|
+
sourceIcon,
|
|
169
|
+
sourceDescription,
|
|
170
|
+
currentArticleIndex,
|
|
171
|
+
totalArticles,
|
|
172
|
+
onNextArticle,
|
|
173
|
+
onPreviousArticle,
|
|
174
|
+
onRejectAllSuggestions,
|
|
175
|
+
onAcceptAllSuggestions: onApproveAllSuggestions
|
|
176
|
+
}
|
|
177
|
+
))), /* @__PURE__ */ React__default.createElement(CardContent, { className: "p-0 [&_ol]:!pl-16 [&_ul]:!pl-16" }, /* @__PURE__ */ React__default.createElement("div", { "aria-hidden": "true", className: "sr-only", hidden: true }, children), /* @__PURE__ */ React__default.createElement("div", { ref: bodyRef }, body))));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const SCROLL_HEIGHT_VAR = "--knowledge-review-scroll-height";
|
|
181
|
+
const KnowledgeReviewScene = React__default.forwardRef(
|
|
182
|
+
function KnowledgeReviewScene2({
|
|
183
|
+
diffTitle = "Knowledge Review",
|
|
184
|
+
children,
|
|
185
|
+
items,
|
|
186
|
+
className,
|
|
187
|
+
onApproveAllSuggestions,
|
|
188
|
+
onRejectAllSuggestions,
|
|
189
|
+
sourceIcon,
|
|
190
|
+
sourceDescription,
|
|
191
|
+
...props
|
|
192
|
+
}, ref) {
|
|
193
|
+
const innerRef = useRef(null);
|
|
194
|
+
useEffect(() => {
|
|
195
|
+
const cardEl = innerRef.current;
|
|
196
|
+
if (!cardEl) return;
|
|
197
|
+
const updateHeight = () => {
|
|
198
|
+
const { height } = cardEl.getBoundingClientRect();
|
|
199
|
+
cardEl.style.setProperty(SCROLL_HEIGHT_VAR, `${height}px`);
|
|
200
|
+
};
|
|
201
|
+
updateHeight();
|
|
202
|
+
if (typeof ResizeObserver !== "function") {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const observer = new ResizeObserver(updateHeight);
|
|
206
|
+
observer.observe(cardEl);
|
|
207
|
+
return () => observer.disconnect();
|
|
208
|
+
}, []);
|
|
209
|
+
const setRef = useCallback(
|
|
210
|
+
(el) => {
|
|
211
|
+
innerRef.current = el;
|
|
212
|
+
if (typeof ref === "function") {
|
|
213
|
+
ref(el);
|
|
214
|
+
} else if (ref) {
|
|
215
|
+
ref.current = el;
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
[ref]
|
|
219
|
+
);
|
|
220
|
+
return /* @__PURE__ */ React__default.createElement(
|
|
221
|
+
ReviewsSelectionProvider,
|
|
222
|
+
{
|
|
223
|
+
defaultSelectedItem: items.find((m) => m.isSelected) ?? null,
|
|
224
|
+
onApproveAllSuggestions,
|
|
225
|
+
onRejectAllSuggestions
|
|
226
|
+
},
|
|
227
|
+
/* @__PURE__ */ React__default.createElement(Card, { className: cn("h-full", className) }, /* @__PURE__ */ React__default.createElement(CardContent, { className: "p-4 lg:p-8 h-full" }, /* @__PURE__ */ React__default.createElement(
|
|
228
|
+
"div",
|
|
229
|
+
{
|
|
230
|
+
ref: setRef,
|
|
231
|
+
className: "grid grid-cols-1 lg:grid-cols-[1fr_4fr] gap-6 min-h-96 h-full",
|
|
232
|
+
style: { [SCROLL_HEIGHT_VAR]: "100px" },
|
|
233
|
+
...props
|
|
234
|
+
},
|
|
235
|
+
/* @__PURE__ */ React__default.createElement(ReviewList, { items, className: "min-w-80 lg:block hidden" }),
|
|
236
|
+
/* @__PURE__ */ React__default.createElement(
|
|
237
|
+
KnowledgeReviewPanel,
|
|
238
|
+
{
|
|
239
|
+
diffTitle,
|
|
240
|
+
items,
|
|
241
|
+
sourceIcon,
|
|
242
|
+
sourceDescription
|
|
243
|
+
},
|
|
244
|
+
children
|
|
245
|
+
)
|
|
246
|
+
)))
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
export { KnowledgeReviewDiffLoader, KnowledgeReviewPanel, KnowledgeReviewScene, ReviewsSelectionProvider, useReviewsSelection };
|
|
252
|
+
//# sourceMappingURL=knowledge-review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge-review.js","sources":["../../../src/scenes/knowledge-review/diff-loader.tsx","../../../src/scenes/knowledge-review/suggestions-header/suggestions-header.tsx","../../../src/lib/countSuggestions.ts","../../../src/scenes/knowledge-review/knowledge-review-panel.tsx","../../../src/scenes/knowledge-review/scene.tsx"],"sourcesContent":["import { useCallback, useEffect } from 'react';\n\nimport type { LoadedDiffDoc } from './context';\nimport { useReviewsSelection } from './context';\n\nexport type KnowledgeReviewDiffLoaderProps = {\n /** Called when selectedItem changes. Return diff payload or null. */\n fetchDiffForItem: (itemId: string) => Promise<LoadedDiffDoc | null>;\n};\n\n/**\n * Listens to selectedItem from context and fetches diff via the provided callback.\n * Owns loading state and context updates; host (Storybook or Platform) supplies fetch logic.\n */\nexport function KnowledgeReviewDiffLoader({ fetchDiffForItem }: KnowledgeReviewDiffLoaderProps) {\n const { selectedItem, setLoadedDiffDoc, setLoadingDiff } = useReviewsSelection();\n\n const loadDiffForItem = useCallback(\n async (itemId: string, signal: AbortSignal) => {\n setLoadingDiff(true);\n setLoadedDiffDoc(null);\n\n try {\n const doc = await fetchDiffForItem(itemId);\n if (signal.aborted) return;\n setLoadedDiffDoc(doc);\n } catch (error) {\n if (signal.aborted) return;\n console.error('Failed to fetch diff:', error);\n setLoadedDiffDoc(null);\n } finally {\n if (!signal.aborted) {\n setLoadingDiff(false);\n }\n }\n },\n [fetchDiffForItem, setLoadedDiffDoc, setLoadingDiff],\n );\n\n const selectedId = selectedItem?.id;\n\n useEffect(() => {\n if (!selectedId) {\n setLoadedDiffDoc(null);\n setLoadingDiff(false);\n\n return;\n }\n\n const abortController = new AbortController();\n void loadDiffForItem(selectedId, abortController.signal);\n\n return () => {\n abortController.abort();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedId, loadDiffForItem]);\n\n return null;\n}\n","import { CaretDown, CaretUp, Backspace, CheckCircle } from '@phosphor-icons/react';\nimport * as React from 'react';\n\nimport { EditCount } from '../review-list/edit-count';\n\nimport { Button } from '@/components/ui/button';\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';\nimport { ButtonGroup } from '@/components/ui/button-group';\nimport { cn } from '@/lib/utils';\nimport { dark } from '@/colors/dark';\nimport { StatusBadge } from '@/components/convos/status-badge';\n\nexport interface SuggestionsHeaderProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {\n title: React.ReactNode;\n isNew: boolean;\n suggestionsCount: number;\n sourceIcon: React.ReactNode;\n sourceDescription: string;\n currentArticleIndex: number;\n totalArticles: number;\n onNextArticle: () => void;\n onPreviousArticle: () => void;\n onRejectAllSuggestions: () => void;\n onAcceptAllSuggestions: () => void;\n}\n\nexport const SuggestionsHeader = React.forwardRef<HTMLDivElement, SuggestionsHeaderProps>(function SuggestionsHeader(\n {\n title,\n isNew,\n suggestionsCount,\n sourceIcon,\n sourceDescription,\n currentArticleIndex,\n totalArticles,\n onNextArticle,\n onPreviousArticle,\n onRejectAllSuggestions,\n onAcceptAllSuggestions,\n className,\n ...props\n },\n ref,\n) {\n const hasNextArticle = totalArticles > 0 && currentArticleIndex < totalArticles - 1;\n const hasPreviousArticle = currentArticleIndex > 0;\n const currentArticle = totalArticles === 0 ? 0 : currentArticleIndex + 1;\n const hasArticles = totalArticles > 0;\n\n let status: React.ReactNode | undefined;\n if (isNew) {\n status = <StatusBadge variant=\"success\">New</StatusBadge>;\n } else if (suggestionsCount) {\n status = <EditCount count={suggestionsCount} />;\n }\n\n return (\n <div ref={ref} className={cn('flex flex-col gap-4', className)} {...props}>\n {/* Header + Navigation */}\n <div className=\"flex justify-between items-center\">\n <h1 className=\"heading-lg\">{title}</h1>\n <div className=\"flex items-center\">\n <p className=\"text-xs text-foreground\">\n <span aria-hidden=\"true\">{hasArticles ? `${currentArticle}/${totalArticles}` : '0'}</span>\n <span className=\"sr-only\" aria-live=\"polite\">\n {hasArticles ? `Article ${currentArticle} of ${totalArticles}` : 'No articles'}\n </span>\n </p>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n disabled={!hasNextArticle}\n onClick={onNextArticle}\n aria-label=\"Go to next article\"\n >\n <CaretDown aria-hidden=\"true\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent side=\"bottom\" borderColor=\"transparent\" background={dark[900]} textColor={dark[100]}>\n Next Article\n </TooltipContent>\n </Tooltip>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n disabled={!hasPreviousArticle}\n onClick={onPreviousArticle}\n aria-label=\"Go to previous article\"\n >\n <CaretUp aria-hidden=\"true\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent side=\"top\" borderColor=\"transparent\" background={dark[900]} textColor={dark[100]}>\n Previous Article\n </TooltipContent>\n </Tooltip>\n </div>\n </div>\n\n {/* Status, Source + Actions */}\n <div className=\"flex-1 px-2 pb-2 flex justify-between items-center\">\n <div className=\"flex items-center gap-2\">\n {status}\n {sourceIcon && <span className=\"shrink-0 flex items-center\">{sourceIcon}</span>}\n {sourceDescription && <span className=\"text-xs text-subtle\">{sourceDescription}</span>}\n </div>\n <ButtonGroup orientation=\"horizontal\" rounded={false} aria-label=\"Suggestion actions\" className=\"gap-1\">\n <Button variant=\"ghost\" onClick={onRejectAllSuggestions}>\n <Backspace aria-hidden=\"true\" />\n Reject\n </Button>\n <Button variant=\"shadow\" onClick={onAcceptAllSuggestions}>\n <CheckCircle aria-hidden=\"true\" />\n Accept\n </Button>\n </ButtonGroup>\n </div>\n </div>\n );\n});\n","function countSuggestionsInRoot(root: Document | Element): number {\n const adjacentPairs = root.querySelectorAll('del + ins');\n const standaloneIns = root.querySelectorAll('ins:not(del + ins)');\n const allDel = root.querySelectorAll('del');\n const standaloneDel = Array.from(allDel).filter((del) => del.nextElementSibling?.tagName !== 'INS');\n\n return adjacentPairs.length + standaloneIns.length + standaloneDel.length;\n}\n\n/** Count suggestions in an HTML string (for title or raw HTML content). */\nexport const countSuggestions = (htmlString: string) => {\n if (!htmlString || typeof DOMParser === 'undefined') {\n return 0;\n }\n const parser = new DOMParser();\n const doc = parser.parseFromString(htmlString, 'text/html');\n\n return countSuggestionsInRoot(doc);\n};\n\n/** Count suggestions inside a DOM element (for rendered ReactNode body content). */\nexport const countSuggestionsInElement = (element: Element | null) => {\n if (!element) return 0;\n\n return countSuggestionsInRoot(element);\n};\n","import React, { useCallback, useEffect, useRef, useState } from 'react';\nimport parse from 'html-react-parser';\n\nimport { useReviewsSelection } from './context';\nimport { SuggestionsHeader } from './suggestions-header';\nimport type { ReviewListItemProps } from './review-list';\n\nimport { countSuggestions, countSuggestionsInElement } from '@/lib/countSuggestions';\nimport { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';\nimport { ScrollArea } from '@/components/ui/scroll-area';\n\nexport type KnowledgeReviewPanelProps = {\n diffTitle: string;\n items: ReviewListItemProps[];\n children?: React.ReactNode;\n sourceIcon: React.ReactNode;\n sourceDescription: string;\n};\n\n/**\n * Panel that shows the selected diff (title + content), suggestion count, and article nav.\n * Must be used inside ReviewsSelectionProvider. Expects --knowledge-review-scroll-height\n * to be set by a parent for ScrollArea height.\n */\nexport function KnowledgeReviewPanel({\n diffTitle,\n items,\n children,\n sourceIcon,\n sourceDescription,\n}: KnowledgeReviewPanelProps) {\n const { selectedItem, setSelectedItem, loadedDiffDoc, loadingDiff, onApproveAllSuggestions, onRejectAllSuggestions } =\n useReviewsSelection();\n\n const title = loadingDiff ? '' : parse(loadedDiffDoc?.diffTitle ?? diffTitle);\n const body = loadingDiff ? <div className=\"text-muted-foreground\">Loading diff…</div> : loadedDiffDoc?.diffContent;\n\n const titleSuggestionsCount = loadingDiff ? 0 : countSuggestions(loadedDiffDoc?.diffTitle ?? diffTitle);\n\n const bodyRef = useRef<HTMLDivElement>(null);\n const [bodySuggestionsCount, setBodySuggestionsCount] = useState(0);\n useEffect(() => {\n if (loadingDiff || !loadedDiffDoc) {\n setBodySuggestionsCount(0);\n\n return;\n }\n const el = bodyRef.current;\n if (!el) return;\n const id = requestAnimationFrame(() => {\n setBodySuggestionsCount(countSuggestionsInElement(el));\n });\n\n return () => cancelAnimationFrame(id);\n }, [loadingDiff, loadedDiffDoc]);\n\n const totalSuggestionsCount = titleSuggestionsCount + bodySuggestionsCount;\n\n const totalArticles = items.length;\n const currentArticleIndex = selectedItem\n ? Math.max(\n 0,\n items.findIndex((i) => i.id === selectedItem.id),\n )\n : 0;\n\n const onNextArticle = useCallback(() => {\n if (currentArticleIndex < totalArticles - 1) {\n setSelectedItem(items[currentArticleIndex + 1] ?? null);\n }\n }, [currentArticleIndex, items, setSelectedItem, totalArticles]);\n\n const onPreviousArticle = useCallback(() => {\n if (currentArticleIndex > 0) {\n setSelectedItem(items[currentArticleIndex - 1] ?? null);\n }\n }, [currentArticleIndex, items, setSelectedItem]);\n\n return (\n <Card className=\"py-6 pl-4\" data-name=\"knowledge-reviews\">\n <ScrollArea className=\"h-[calc(var(--knowledge-review-scroll-height)-50px)] pr-8\">\n <CardHeader className=\"p-0\">\n <CardTitle className=\"border-b border-dark-300 mb-6 pb-2\">\n <SuggestionsHeader\n title={title}\n isNew={loadedDiffDoc?.isNew ?? false}\n suggestionsCount={totalSuggestionsCount}\n sourceIcon={sourceIcon}\n sourceDescription={sourceDescription}\n currentArticleIndex={currentArticleIndex}\n totalArticles={totalArticles}\n onNextArticle={onNextArticle}\n onPreviousArticle={onPreviousArticle}\n onRejectAllSuggestions={onRejectAllSuggestions}\n onAcceptAllSuggestions={onApproveAllSuggestions}\n />\n </CardTitle>\n </CardHeader>\n <CardContent className=\"p-0 [&_ol]:!pl-16 [&_ul]:!pl-16\">\n <div aria-hidden=\"true\" className=\"sr-only\" hidden>\n {children}\n </div>\n <div ref={bodyRef}>{body}</div>\n </CardContent>\n </ScrollArea>\n </Card>\n );\n}\n","import React, { useCallback, useEffect, useRef } from 'react';\n\nimport { ReviewList, ReviewListItemProps, ReviewsSelectionProvider } from './review-list';\nimport { KnowledgeReviewPanel } from './knowledge-review-panel';\n\nimport { Card, CardContent } from '@/components/ui/card';\nimport { cn } from '@/lib/utils';\n\nimport './knowledge-review.css';\n\nconst SCROLL_HEIGHT_VAR = '--knowledge-review-scroll-height';\n\nexport type KnowledgeReviewSceneProps = {\n diffTitle?: string;\n children: React.ReactNode;\n items: ReviewListItemProps[];\n className?: string;\n onApproveAllSuggestions: () => void;\n onRejectAllSuggestions: () => void;\n sourceIcon: React.ReactNode;\n sourceDescription: string;\n};\n\nexport const KnowledgeReviewScene = React.forwardRef<HTMLDivElement, KnowledgeReviewSceneProps>(\n function KnowledgeReviewScene(\n {\n diffTitle = 'Knowledge Review',\n children,\n items,\n className,\n onApproveAllSuggestions,\n onRejectAllSuggestions,\n sourceIcon,\n sourceDescription,\n ...props\n },\n ref,\n ) {\n const innerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const cardEl = innerRef.current;\n if (!cardEl) return;\n\n const updateHeight = () => {\n const { height } = cardEl.getBoundingClientRect();\n cardEl.style.setProperty(SCROLL_HEIGHT_VAR, `${height}px`);\n };\n\n updateHeight();\n\n if (typeof ResizeObserver !== 'function') {\n return;\n }\n\n const observer = new ResizeObserver(updateHeight);\n observer.observe(cardEl);\n\n return () => observer.disconnect();\n }, []);\n\n const setRef = useCallback(\n (el: HTMLDivElement | null) => {\n (innerRef as React.MutableRefObject<HTMLDivElement | null>).current = el;\n if (typeof ref === 'function') {\n ref(el);\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLDivElement | null>).current = el;\n }\n },\n [ref],\n );\n\n return (\n <ReviewsSelectionProvider\n defaultSelectedItem={items.find((m) => m.isSelected) ?? null}\n onApproveAllSuggestions={onApproveAllSuggestions}\n onRejectAllSuggestions={onRejectAllSuggestions}\n >\n <Card className={cn('h-full', className)}>\n <CardContent className=\"p-4 lg:p-8 h-full\">\n <div\n ref={setRef}\n className=\"grid grid-cols-1 lg:grid-cols-[1fr_4fr] gap-6 min-h-96 h-full\"\n style={{ [SCROLL_HEIGHT_VAR]: '100px' } as React.CSSProperties}\n {...props}\n >\n <ReviewList items={items} className=\"min-w-80 lg:block hidden\" />\n <KnowledgeReviewPanel\n diffTitle={diffTitle}\n items={items}\n sourceIcon={sourceIcon}\n sourceDescription={sourceDescription}\n >\n {children}\n </KnowledgeReviewPanel>\n </div>\n </CardContent>\n </Card>\n </ReviewsSelectionProvider>\n );\n },\n);\n"],"names":["SuggestionsHeader","React","KnowledgeReviewScene"],"mappings":"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"}
|
package/dist/full-layout.d.ts
CHANGED
|
@@ -170,11 +170,16 @@ declare interface SidebarContextProps {
|
|
|
170
170
|
onSearchArticles?: (value: string) => void;
|
|
171
171
|
onAddArticle?: (parentId: string | null) => void;
|
|
172
172
|
onMoreActions?: (articleId: string) => void;
|
|
173
|
+
canMoveArticle?: (move: {
|
|
174
|
+
id: string;
|
|
175
|
+
parentId: string | null;
|
|
176
|
+
index: number;
|
|
177
|
+
}) => boolean;
|
|
173
178
|
onMoveArticle?: (moved: {
|
|
174
179
|
id: string;
|
|
175
180
|
parentId: string | null;
|
|
176
181
|
index: number;
|
|
177
|
-
}) => void
|
|
182
|
+
}) => void | Promise<void>;
|
|
178
183
|
}
|
|
179
184
|
|
|
180
185
|
declare interface SidebarNavItem {
|
package/dist/index.d.ts
CHANGED
|
@@ -1322,6 +1322,12 @@ declare namespace LeftSidebar {
|
|
|
1322
1322
|
var displayName: string;
|
|
1323
1323
|
}
|
|
1324
1324
|
|
|
1325
|
+
export declare type LoadedDiffDoc = {
|
|
1326
|
+
isNew: boolean;
|
|
1327
|
+
diffTitle: string;
|
|
1328
|
+
diffContent: default_2.ReactNode;
|
|
1329
|
+
};
|
|
1330
|
+
|
|
1325
1331
|
/**
|
|
1326
1332
|
* Load a Google Font stylesheet dynamically
|
|
1327
1333
|
*/
|
|
@@ -1493,17 +1499,25 @@ declare interface ReviewListProps {
|
|
|
1493
1499
|
className?: string;
|
|
1494
1500
|
}
|
|
1495
1501
|
|
|
1496
|
-
declare type ReviewsProviderProps = {
|
|
1502
|
+
export declare type ReviewsProviderProps = {
|
|
1497
1503
|
children: default_2.ReactNode;
|
|
1498
1504
|
/** Optional initial selection (e.g. from server or mock data) */
|
|
1499
1505
|
defaultSelectedItem?: ReviewListItemProps | null;
|
|
1506
|
+
onApproveAllSuggestions: () => void;
|
|
1507
|
+
onRejectAllSuggestions: () => void;
|
|
1500
1508
|
};
|
|
1501
1509
|
|
|
1502
|
-
export declare function ReviewsSelectionProvider({ children, defaultSelectedItem }: ReviewsProviderProps): default_2.JSX.Element;
|
|
1510
|
+
export declare function ReviewsSelectionProvider({ children, defaultSelectedItem, onApproveAllSuggestions, onRejectAllSuggestions, }: ReviewsProviderProps): default_2.JSX.Element;
|
|
1503
1511
|
|
|
1504
1512
|
declare type ReviewsSelectionValue = {
|
|
1505
1513
|
selectedItem: ReviewListItemProps | null;
|
|
1506
1514
|
setSelectedItem: (item: ReviewListItemProps | null) => void;
|
|
1515
|
+
loadedDiffDoc: LoadedDiffDoc | null;
|
|
1516
|
+
setLoadedDiffDoc: (doc: LoadedDiffDoc | null) => void;
|
|
1517
|
+
loadingDiff: boolean;
|
|
1518
|
+
setLoadingDiff: (loading: boolean) => void;
|
|
1519
|
+
onApproveAllSuggestions: () => void;
|
|
1520
|
+
onRejectAllSuggestions: () => void;
|
|
1507
1521
|
};
|
|
1508
1522
|
|
|
1509
1523
|
declare function RightSidebar({ children }: {
|
|
@@ -1638,11 +1652,16 @@ declare interface SidebarContextProps {
|
|
|
1638
1652
|
onSearchArticles?: (value: string) => void;
|
|
1639
1653
|
onAddArticle?: (parentId: string | null) => void;
|
|
1640
1654
|
onMoreActions?: (articleId: string) => void;
|
|
1655
|
+
canMoveArticle?: (move: {
|
|
1656
|
+
id: string;
|
|
1657
|
+
parentId: string | null;
|
|
1658
|
+
index: number;
|
|
1659
|
+
}) => boolean;
|
|
1641
1660
|
onMoveArticle?: (moved: {
|
|
1642
1661
|
id: string;
|
|
1643
1662
|
parentId: string | null;
|
|
1644
1663
|
index: number;
|
|
1645
|
-
}) => void
|
|
1664
|
+
}) => void | Promise<void>;
|
|
1646
1665
|
}
|
|
1647
1666
|
|
|
1648
1667
|
export declare interface SidebarContextValue extends SidebarContextProps {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { default as default_2 } from 'react';
|
|
2
|
+
import * as React_2 from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Listens to selectedItem from context and fetches diff via the provided callback.
|
|
6
|
+
* Owns loading state and context updates; host (Storybook or Platform) supplies fetch logic.
|
|
7
|
+
*/
|
|
8
|
+
export declare function KnowledgeReviewDiffLoader({ fetchDiffForItem }: KnowledgeReviewDiffLoaderProps): null;
|
|
9
|
+
|
|
10
|
+
export declare type KnowledgeReviewDiffLoaderProps = {
|
|
11
|
+
/** Called when selectedItem changes. Return diff payload or null. */
|
|
12
|
+
fetchDiffForItem: (itemId: string) => Promise<LoadedDiffDoc | null>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Panel that shows the selected diff (title + content), suggestion count, and article nav.
|
|
17
|
+
* Must be used inside ReviewsSelectionProvider. Expects --knowledge-review-scroll-height
|
|
18
|
+
* to be set by a parent for ScrollArea height.
|
|
19
|
+
*/
|
|
20
|
+
export declare function KnowledgeReviewPanel({ diffTitle, items, children, sourceIcon, sourceDescription, }: KnowledgeReviewPanelProps): default_2.JSX.Element;
|
|
21
|
+
|
|
22
|
+
export declare type KnowledgeReviewPanelProps = {
|
|
23
|
+
diffTitle: string;
|
|
24
|
+
items: ReviewListItemProps[];
|
|
25
|
+
children?: default_2.ReactNode;
|
|
26
|
+
sourceIcon: default_2.ReactNode;
|
|
27
|
+
sourceDescription: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export declare const KnowledgeReviewScene: default_2.ForwardRefExoticComponent<KnowledgeReviewSceneProps & default_2.RefAttributes<HTMLDivElement>>;
|
|
31
|
+
|
|
32
|
+
export declare type KnowledgeReviewSceneProps = {
|
|
33
|
+
diffTitle?: string;
|
|
34
|
+
children: default_2.ReactNode;
|
|
35
|
+
items: ReviewListItemProps[];
|
|
36
|
+
className?: string;
|
|
37
|
+
onApproveAllSuggestions: () => void;
|
|
38
|
+
onRejectAllSuggestions: () => void;
|
|
39
|
+
sourceIcon: default_2.ReactNode;
|
|
40
|
+
sourceDescription: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export declare type LoadedDiffDoc = {
|
|
44
|
+
isNew: boolean;
|
|
45
|
+
diffTitle: string;
|
|
46
|
+
diffContent: default_2.ReactNode;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export declare interface ReviewListItemProps extends React_2.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
50
|
+
/** Unique identifier for the knowledge article */
|
|
51
|
+
id: string;
|
|
52
|
+
/** Primary text displayed in the first row */
|
|
53
|
+
title: string;
|
|
54
|
+
/** Content rendered in the top-right corner (e.g. relative timestamp) */
|
|
55
|
+
timestamp: Date;
|
|
56
|
+
/** Icon rendered at the start of the second row */
|
|
57
|
+
icon?: React_2.ReactNode;
|
|
58
|
+
/** Text or content next to the icon in the second row */
|
|
59
|
+
description?: React_2.ReactNode;
|
|
60
|
+
/** Whether this item is new */
|
|
61
|
+
isNew?: boolean;
|
|
62
|
+
/** Number of edits for this item */
|
|
63
|
+
editCount?: number;
|
|
64
|
+
/** Whether this item is currently selected */
|
|
65
|
+
isSelected?: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
declare type ReviewsProviderProps = {
|
|
69
|
+
children: default_2.ReactNode;
|
|
70
|
+
/** Optional initial selection (e.g. from server or mock data) */
|
|
71
|
+
defaultSelectedItem?: ReviewListItemProps | null;
|
|
72
|
+
onApproveAllSuggestions: () => void;
|
|
73
|
+
onRejectAllSuggestions: () => void;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export declare function ReviewsSelectionProvider({ children, defaultSelectedItem, onApproveAllSuggestions, onRejectAllSuggestions, }: ReviewsProviderProps): default_2.JSX.Element;
|
|
77
|
+
|
|
78
|
+
declare type ReviewsSelectionValue = {
|
|
79
|
+
selectedItem: ReviewListItemProps | null;
|
|
80
|
+
setSelectedItem: (item: ReviewListItemProps | null) => void;
|
|
81
|
+
loadedDiffDoc: LoadedDiffDoc | null;
|
|
82
|
+
setLoadedDiffDoc: (doc: LoadedDiffDoc | null) => void;
|
|
83
|
+
loadingDiff: boolean;
|
|
84
|
+
setLoadingDiff: (loading: boolean) => void;
|
|
85
|
+
onApproveAllSuggestions: () => void;
|
|
86
|
+
onRejectAllSuggestions: () => void;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export declare function useReviewsSelection(): ReviewsSelectionValue;
|
|
90
|
+
|
|
91
|
+
export { }
|
package/dist/sidebar.d.ts
CHANGED
|
@@ -21,11 +21,16 @@ declare interface SidebarContextProps {
|
|
|
21
21
|
onSearchArticles?: (value: string) => void;
|
|
22
22
|
onAddArticle?: (parentId: string | null) => void;
|
|
23
23
|
onMoreActions?: (articleId: string) => void;
|
|
24
|
+
canMoveArticle?: (move: {
|
|
25
|
+
id: string;
|
|
26
|
+
parentId: string | null;
|
|
27
|
+
index: number;
|
|
28
|
+
}) => boolean;
|
|
24
29
|
onMoveArticle?: (moved: {
|
|
25
30
|
id: string;
|
|
26
31
|
parentId: string | null;
|
|
27
32
|
index: number;
|
|
28
|
-
}) => void
|
|
33
|
+
}) => void | Promise<void>;
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
export declare interface SidebarContextValue extends SidebarContextProps {
|