@alpaca-editor/core 1.0.4057 → 1.0.4061
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/components/ui/badge.js.map +1 -1
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/checkbox.js +1 -1
- package/dist/components/ui/checkbox.js.map +1 -1
- package/dist/components/ui/tooltip.js +2 -2
- package/dist/components/ui/tooltip.js.map +1 -1
- package/dist/config/config.js +7 -3
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +1 -0
- package/dist/editor/FieldListField.js +3 -2
- package/dist/editor/FieldListField.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.js +0 -3
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/client/EditorClient.js +20 -6
- package/dist/editor/client/EditorClient.js.map +1 -1
- package/dist/editor/client/fieldModificationStore.d.ts +1 -0
- package/dist/editor/client/fieldModificationStore.js +19 -18
- package/dist/editor/client/fieldModificationStore.js.map +1 -1
- package/dist/editor/client/operations.js +1 -2
- package/dist/editor/client/operations.js.map +1 -1
- package/dist/editor/control-center/IndexOverview.js +278 -18
- package/dist/editor/control-center/IndexOverview.js.map +1 -1
- package/dist/editor/control-center/Status.js +1 -1
- package/dist/editor/control-center/Status.js.map +1 -1
- package/dist/editor/field-types/CheckboxEditor.js +1 -1
- package/dist/editor/field-types/CheckboxEditor.js.map +1 -1
- package/dist/editor/media-selector/AiImageSearch.js +2 -1
- package/dist/editor/media-selector/AiImageSearch.js.map +1 -1
- package/dist/editor/media-selector/TreeSelector.js +1 -1
- package/dist/editor/media-selector/TreeSelector.js.map +1 -1
- package/dist/editor/menubar/PageSelector.js +1 -1
- package/dist/editor/menubar/PageSelector.js.map +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js +1 -1
- package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
- package/dist/editor/reviews/Comment.d.ts +5 -1
- package/dist/editor/reviews/Comment.js +64 -92
- package/dist/editor/reviews/Comment.js.map +1 -1
- package/dist/editor/reviews/CommentDisplayPopover.js +55 -49
- package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
- package/dist/editor/reviews/CommentEditor.d.ts +20 -0
- package/dist/editor/reviews/CommentEditor.js +57 -0
- package/dist/editor/reviews/CommentEditor.js.map +1 -0
- package/dist/editor/reviews/CommentPopover.js +34 -43
- package/dist/editor/reviews/CommentPopover.js.map +1 -1
- package/dist/editor/reviews/CommentView.d.ts +19 -0
- package/dist/editor/reviews/CommentView.js +46 -0
- package/dist/editor/reviews/CommentView.js.map +1 -0
- package/dist/editor/reviews/Comments.js +28 -1
- package/dist/editor/reviews/Comments.js.map +1 -1
- package/dist/editor/reviews/SuggestionDisplayPopover.js +1 -1
- package/dist/editor/reviews/SuggestionDisplayPopover.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +0 -8
- package/dist/editor/services/aiService.js +1 -24
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/services/indexService.d.ts +12 -6
- package/dist/editor/services/indexService.js +33 -11
- package/dist/editor/services/indexService.js.map +1 -1
- package/dist/editor/services/reviewsService.d.ts +5 -1
- package/dist/editor/services/reviewsService.js +15 -0
- package/dist/editor/services/reviewsService.js.map +1 -1
- package/dist/editor/services/searchService.d.ts +17 -0
- package/dist/editor/services/searchService.js +25 -0
- package/dist/editor/services/searchService.js.map +1 -0
- package/dist/editor/sidebar/ViewSelector.js +7 -6
- package/dist/editor/sidebar/ViewSelector.js.map +1 -1
- package/dist/editor/ui/CenteredMessage.js +1 -1
- package/dist/editor/ui/CenteredMessage.js.map +1 -1
- package/dist/editor/ui/ItemSearch.js +1 -1
- package/dist/editor/ui/ItemSearch.js.map +1 -1
- package/dist/editor/ui/SimpleToolbar.js +1 -1
- package/dist/editor/ui/SimpleToolbar.js.map +1 -1
- package/dist/editor/ui/Spinner.d.ts +2 -1
- package/dist/editor/ui/Spinner.js +3 -2
- package/dist/editor/ui/Spinner.js.map +1 -1
- package/dist/editor/utils/keyboardNavigation.js +3 -1
- package/dist/editor/utils/keyboardNavigation.js.map +1 -1
- package/dist/page-wizard/steps/ImagesStep.js +2 -1
- package/dist/page-wizard/steps/ImagesStep.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/styles.css +76 -12
- package/dist/types.d.ts +61 -19
- package/package.json +1 -1
- package/src/components/ui/badge.tsx +1 -1
- package/src/components/ui/button.tsx +1 -1
- package/src/components/ui/checkbox.tsx +1 -1
- package/src/components/ui/tooltip.tsx +5 -3
- package/src/config/config.tsx +9 -3
- package/src/config/types.ts +1 -0
- package/src/editor/FieldListField.tsx +51 -32
- package/src/editor/ai/AgentTerminal.tsx +0 -6
- package/src/editor/client/EditorClient.tsx +47 -15
- package/src/editor/client/fieldModificationStore.ts +19 -19
- package/src/editor/client/operations.ts +1 -3
- package/src/editor/control-center/IndexOverview.tsx +590 -36
- package/src/editor/control-center/Status.tsx +1 -1
- package/src/editor/field-types/CheckboxEditor.tsx +1 -0
- package/src/editor/media-selector/AiImageSearch.tsx +2 -1
- package/src/editor/media-selector/TreeSelector.tsx +1 -1
- package/src/editor/menubar/PageSelector.tsx +1 -1
- package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +1 -1
- package/src/editor/reviews/Comment.tsx +105 -244
- package/src/editor/reviews/CommentDisplayPopover.tsx +90 -219
- package/src/editor/reviews/CommentEditor.tsx +153 -0
- package/src/editor/reviews/CommentPopover.tsx +42 -70
- package/src/editor/reviews/CommentView.tsx +229 -0
- package/src/editor/reviews/Comments.tsx +33 -1
- package/src/editor/reviews/SuggestionDisplayPopover.tsx +1 -1
- package/src/editor/services/aiService.ts +1 -39
- package/src/editor/services/indexService.ts +58 -12
- package/src/editor/services/reviewsService.ts +34 -1
- package/src/editor/services/searchService.ts +45 -0
- package/src/editor/sidebar/ViewSelector.tsx +40 -27
- package/src/editor/ui/CenteredMessage.tsx +1 -1
- package/src/editor/ui/ItemSearch.tsx +1 -1
- package/src/editor/ui/SimpleToolbar.tsx +1 -1
- package/src/editor/ui/Spinner.tsx +8 -1
- package/src/editor/utils/keyboardNavigation.ts +3 -1
- package/src/page-wizard/steps/ImagesStep.tsx +2 -1
- package/src/revision.ts +2 -2
- package/src/types.ts +74 -20
- package/dist/editor/control-center/IndexSettings.d.ts +0 -5
- package/dist/editor/control-center/IndexSettings.js +0 -104
- package/dist/editor/control-center/IndexSettings.js.map +0 -1
- package/src/editor/control-center/IndexSettings.tsx +0 -266
|
@@ -1,49 +1,603 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
RefreshCw,
|
|
4
|
+
Database,
|
|
5
|
+
Clock,
|
|
6
|
+
CheckCircle,
|
|
7
|
+
AlertCircle,
|
|
8
|
+
Trash2,
|
|
9
|
+
} from "lucide-react";
|
|
4
10
|
|
|
5
|
-
import { IndexSettings } from "./IndexSettings";
|
|
6
11
|
import { classNames } from "primereact/utils";
|
|
12
|
+
import {
|
|
13
|
+
IndexStatus,
|
|
14
|
+
StagingStatus,
|
|
15
|
+
ImportStatus,
|
|
16
|
+
CentroidsStatus,
|
|
17
|
+
} from "../../types";
|
|
18
|
+
import {
|
|
19
|
+
getIndexStatus,
|
|
20
|
+
cleanupBatches,
|
|
21
|
+
populateCentroids,
|
|
22
|
+
processCompletedBatches,
|
|
23
|
+
collectAndUpload,
|
|
24
|
+
submitStaged,
|
|
25
|
+
getStagingStatus,
|
|
26
|
+
getImportStatus,
|
|
27
|
+
getCentroidsStatus,
|
|
28
|
+
startDirectGeneration,
|
|
29
|
+
} from "../services/indexService";
|
|
7
30
|
|
|
8
31
|
export function IndexOverview() {
|
|
9
|
-
const [
|
|
10
|
-
const [
|
|
11
|
-
|
|
12
|
-
);
|
|
32
|
+
const [indexStatus, setIndexStatus] = useState<IndexStatus | null>(null);
|
|
33
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
34
|
+
const [isCleaning, setIsCleaning] = useState(false);
|
|
35
|
+
const [isPopulating, setIsPopulating] = useState(false);
|
|
36
|
+
const [kCentroids, setKCentroids] = useState<number>(1024);
|
|
37
|
+
const [error, setError] = useState<string | null>(null);
|
|
38
|
+
const [isFinalizing, setIsFinalizing] = useState(false);
|
|
39
|
+
const [staging, setStaging] = useState<StagingStatus | null>(null);
|
|
40
|
+
const [importStatus, setImportStatus] = useState<ImportStatus | null>(null);
|
|
41
|
+
const [centroidsStatus, setCentroidsStatus] =
|
|
42
|
+
useState<CentroidsStatus | null>(null);
|
|
43
|
+
const [isCollecting, setIsCollecting] = useState(false);
|
|
44
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
45
|
+
const [isStartingDirect, setIsStartingDirect] = useState(false);
|
|
46
|
+
|
|
47
|
+
const fetchStatus = async () => {
|
|
48
|
+
try {
|
|
49
|
+
setIsLoading(true);
|
|
50
|
+
setError(null);
|
|
51
|
+
const status = await getIndexStatus();
|
|
52
|
+
setIndexStatus(status || null);
|
|
53
|
+
const st = (await getStagingStatus()) as StagingStatus | null;
|
|
54
|
+
setStaging(st || null);
|
|
55
|
+
const importSt = (await getImportStatus()) as ImportStatus | null;
|
|
56
|
+
setImportStatus(importSt || null);
|
|
57
|
+
const centroidsSt =
|
|
58
|
+
(await getCentroidsStatus()) as CentroidsStatus | null;
|
|
59
|
+
setCentroidsStatus(centroidsSt || null);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
setError(
|
|
62
|
+
err instanceof Error ? err.message : "Failed to fetch index status",
|
|
63
|
+
);
|
|
64
|
+
} finally {
|
|
65
|
+
setIsLoading(false);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const handleCollectAndUpload = async () => {
|
|
70
|
+
try {
|
|
71
|
+
setIsCollecting(true);
|
|
72
|
+
setError(null);
|
|
73
|
+
await collectAndUpload();
|
|
74
|
+
await fetchStatus();
|
|
75
|
+
} catch (err) {
|
|
76
|
+
setError(
|
|
77
|
+
err instanceof Error ? err.message : "Failed to collect and upload",
|
|
78
|
+
);
|
|
79
|
+
} finally {
|
|
80
|
+
setIsCollecting(false);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const handleSubmitStaged = async () => {
|
|
85
|
+
try {
|
|
86
|
+
setIsSubmitting(true);
|
|
87
|
+
setError(null);
|
|
88
|
+
await submitStaged();
|
|
89
|
+
await fetchStatus();
|
|
90
|
+
} catch (err) {
|
|
91
|
+
setError(
|
|
92
|
+
err instanceof Error ? err.message : "Failed to submit staged files",
|
|
93
|
+
);
|
|
94
|
+
} finally {
|
|
95
|
+
setIsSubmitting(false);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const handleStartDirect = async () => {
|
|
100
|
+
try {
|
|
101
|
+
setIsStartingDirect(true);
|
|
102
|
+
setError(null);
|
|
103
|
+
await startDirectGeneration();
|
|
104
|
+
await fetchStatus();
|
|
105
|
+
} catch (err) {
|
|
106
|
+
setError(
|
|
107
|
+
err instanceof Error
|
|
108
|
+
? err.message
|
|
109
|
+
: "Failed to start direct generation",
|
|
110
|
+
);
|
|
111
|
+
} finally {
|
|
112
|
+
setIsStartingDirect(false);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const handleCleanup = async () => {
|
|
117
|
+
try {
|
|
118
|
+
setIsCleaning(true);
|
|
119
|
+
setError(null);
|
|
120
|
+
await cleanupBatches();
|
|
121
|
+
await fetchStatus();
|
|
122
|
+
} catch (err) {
|
|
123
|
+
setError(
|
|
124
|
+
err instanceof Error ? err.message : "Failed to cleanup batches",
|
|
125
|
+
);
|
|
126
|
+
} finally {
|
|
127
|
+
setIsCleaning(false);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const handlePopulateCentroids = async () => {
|
|
132
|
+
try {
|
|
133
|
+
setIsPopulating(true);
|
|
134
|
+
setError(null);
|
|
135
|
+
await populateCentroids(kCentroids || 1024);
|
|
136
|
+
await fetchStatus();
|
|
137
|
+
} catch (err) {
|
|
138
|
+
setError(
|
|
139
|
+
err instanceof Error ? err.message : "Failed to populate centroids",
|
|
140
|
+
);
|
|
141
|
+
} finally {
|
|
142
|
+
setIsPopulating(false);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const handleFinalize = async () => {
|
|
147
|
+
try {
|
|
148
|
+
setIsFinalizing(true);
|
|
149
|
+
setError(null);
|
|
150
|
+
await processCompletedBatches();
|
|
151
|
+
await fetchStatus();
|
|
152
|
+
} catch (err) {
|
|
153
|
+
setError(
|
|
154
|
+
err instanceof Error ? err.message : "Failed to finalize rebuild",
|
|
155
|
+
);
|
|
156
|
+
} finally {
|
|
157
|
+
setIsFinalizing(false);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
13
161
|
useEffect(() => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
if (indexes.indexOf("media") === -1) {
|
|
20
|
-
indexes.push("media");
|
|
21
|
-
}
|
|
22
|
-
setIndexes(indexes);
|
|
23
|
-
}
|
|
24
|
-
loadIndexes();
|
|
162
|
+
fetchStatus();
|
|
163
|
+
// Poll for status updates every 60 seconds
|
|
164
|
+
const interval = setInterval(fetchStatus, 60000);
|
|
165
|
+
return () => clearInterval(interval);
|
|
25
166
|
}, []);
|
|
167
|
+
|
|
168
|
+
const getBatchStatusIcon = (status?: string) => {
|
|
169
|
+
switch (status?.toLowerCase()) {
|
|
170
|
+
case "completed":
|
|
171
|
+
return (
|
|
172
|
+
<CheckCircle className="h-4 w-4 text-green-500" strokeWidth={1} />
|
|
173
|
+
);
|
|
174
|
+
case "failed":
|
|
175
|
+
return <AlertCircle className="h-4 w-4 text-red-500" strokeWidth={1} />;
|
|
176
|
+
case "processing":
|
|
177
|
+
return <Clock className="h-4 w-4 text-blue-500" strokeWidth={1} />;
|
|
178
|
+
default:
|
|
179
|
+
return (
|
|
180
|
+
<AlertCircle className="h-4 w-4 text-yellow-500" strokeWidth={1} />
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const getBatchStatusLabelStyles = (status?: string) => {
|
|
186
|
+
switch (status?.toLowerCase()) {
|
|
187
|
+
case "completed":
|
|
188
|
+
return "bg-green-100 text-green-700";
|
|
189
|
+
case "failed":
|
|
190
|
+
return "bg-red-100 text-red-700";
|
|
191
|
+
case "processing":
|
|
192
|
+
return "bg-blue-100 text-blue-700";
|
|
193
|
+
default:
|
|
194
|
+
return "bg-yellow-100 text-yellow-700";
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
26
198
|
return (
|
|
27
|
-
<div className="flex items-stretch
|
|
28
|
-
<div className="flex flex-col gap-
|
|
29
|
-
<div className="flex items-center justify-
|
|
30
|
-
|
|
199
|
+
<div className="flex h-full items-stretch">
|
|
200
|
+
<div className="flex min-w-96 flex-col gap-4 p-4">
|
|
201
|
+
<div className="flex items-center justify-between gap-4 border-b border-gray-200 pb-2">
|
|
202
|
+
<div className="flex items-center gap-2">
|
|
203
|
+
<Database className="h-5 w-5" strokeWidth={1} />
|
|
204
|
+
<span className="font-semibold">Search Index</span>
|
|
205
|
+
</div>
|
|
206
|
+
<div className="flex items-center gap-2">
|
|
207
|
+
{(() => {
|
|
208
|
+
const stagedCount = staging?.fileCount ?? 0;
|
|
209
|
+
const batches = indexStatus?.batches || [];
|
|
210
|
+
const completedCount = batches.filter(
|
|
211
|
+
(b) => (b.status || "").toLowerCase() === "completed",
|
|
212
|
+
).length;
|
|
213
|
+
const inProgressCount = batches.filter((b) => {
|
|
214
|
+
const s = (b.status || "").toLowerCase();
|
|
215
|
+
return (
|
|
216
|
+
s === "submitted" || s === "processing" || s === "queued"
|
|
217
|
+
);
|
|
218
|
+
}).length;
|
|
219
|
+
if (
|
|
220
|
+
isCollecting ||
|
|
221
|
+
isSubmitting ||
|
|
222
|
+
inProgressCount > 0 ||
|
|
223
|
+
indexStatus?.rebuilding ||
|
|
224
|
+
importStatus?.isImporting ||
|
|
225
|
+
centroidsStatus?.isPopulating
|
|
226
|
+
) {
|
|
227
|
+
return null;
|
|
228
|
+
} else if (stagedCount > 0) {
|
|
229
|
+
const batchCost =
|
|
230
|
+
staging?.estimatedBatchCost ?? staging?.estimatedCost ?? 0;
|
|
231
|
+
const directCost = staging?.estimatedDirectCost ?? 0;
|
|
232
|
+
return (
|
|
233
|
+
<div className="flex items-center gap-2">
|
|
234
|
+
<button
|
|
235
|
+
onClick={handleSubmitStaged}
|
|
236
|
+
disabled={isSubmitting || stagedCount === 0}
|
|
237
|
+
className={classNames(
|
|
238
|
+
"flex items-center gap-2 rounded px-3 py-1.5 text-sm font-medium transition-colors",
|
|
239
|
+
{
|
|
240
|
+
"bg-amber-600 text-white hover:bg-amber-700":
|
|
241
|
+
!isSubmitting && stagedCount > 0,
|
|
242
|
+
"cursor-not-allowed bg-gray-300 text-gray-500":
|
|
243
|
+
isSubmitting || stagedCount === 0,
|
|
244
|
+
},
|
|
245
|
+
)}
|
|
246
|
+
>
|
|
247
|
+
<CheckCircle className="h-4 w-4" strokeWidth={1} />
|
|
248
|
+
{isSubmitting
|
|
249
|
+
? "Submitting..."
|
|
250
|
+
: `Batch (~$${batchCost.toFixed(2)})`}
|
|
251
|
+
</button>
|
|
252
|
+
<button
|
|
253
|
+
onClick={handleStartDirect}
|
|
254
|
+
disabled={isStartingDirect || stagedCount === 0}
|
|
255
|
+
className={classNames(
|
|
256
|
+
"flex items-center gap-2 rounded px-3 py-1.5 text-sm font-medium transition-colors",
|
|
257
|
+
{
|
|
258
|
+
"bg-rose-600 text-white hover:bg-rose-700":
|
|
259
|
+
!isStartingDirect && stagedCount > 0,
|
|
260
|
+
"cursor-not-allowed bg-gray-300 text-gray-500":
|
|
261
|
+
isStartingDirect || stagedCount === 0,
|
|
262
|
+
},
|
|
263
|
+
)}
|
|
264
|
+
>
|
|
265
|
+
<CheckCircle className="h-4 w-4" strokeWidth={1} />
|
|
266
|
+
{isStartingDirect
|
|
267
|
+
? "Starting..."
|
|
268
|
+
: `Direct (~$${directCost.toFixed(2)})`}
|
|
269
|
+
</button>
|
|
270
|
+
</div>
|
|
271
|
+
);
|
|
272
|
+
} else if (completedCount > 0) {
|
|
273
|
+
return (
|
|
274
|
+
<button
|
|
275
|
+
onClick={handleFinalize}
|
|
276
|
+
disabled={isFinalizing}
|
|
277
|
+
className={classNames(
|
|
278
|
+
"flex items-center gap-2 rounded px-3 py-1.5 text-sm font-medium transition-colors",
|
|
279
|
+
{
|
|
280
|
+
"bg-green-600 text-white hover:bg-green-700":
|
|
281
|
+
!isFinalizing,
|
|
282
|
+
"cursor-not-allowed bg-gray-300 text-gray-500":
|
|
283
|
+
isFinalizing,
|
|
284
|
+
},
|
|
285
|
+
)}
|
|
286
|
+
>
|
|
287
|
+
<CheckCircle className="h-4 w-4" strokeWidth={1} />
|
|
288
|
+
{isFinalizing ? "Importing..." : "Import embeddings"}
|
|
289
|
+
</button>
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
return (
|
|
293
|
+
<button
|
|
294
|
+
onClick={handleCollectAndUpload}
|
|
295
|
+
disabled={isCollecting}
|
|
296
|
+
className={classNames(
|
|
297
|
+
"flex items-center gap-2 rounded px-3 py-1.5 text-sm font-medium transition-colors",
|
|
298
|
+
{
|
|
299
|
+
"bg-indigo-600 text-white hover:bg-indigo-700":
|
|
300
|
+
!isCollecting,
|
|
301
|
+
"cursor-not-allowed bg-gray-300 text-gray-500":
|
|
302
|
+
isCollecting,
|
|
303
|
+
},
|
|
304
|
+
)}
|
|
305
|
+
>
|
|
306
|
+
<RefreshCw className="h-4 w-4" strokeWidth={1} />
|
|
307
|
+
{isCollecting ? "Collecting..." : "Rebuild Index"}
|
|
308
|
+
</button>
|
|
309
|
+
);
|
|
310
|
+
})()}
|
|
311
|
+
|
|
312
|
+
<div className="flex items-center gap-2">
|
|
313
|
+
<input
|
|
314
|
+
type="number"
|
|
315
|
+
min={1}
|
|
316
|
+
value={kCentroids}
|
|
317
|
+
onChange={(e) =>
|
|
318
|
+
setKCentroids(parseInt(e.target.value) || 1024)
|
|
319
|
+
}
|
|
320
|
+
className="w-24 rounded border border-gray-300 px-2 py-1 text-sm"
|
|
321
|
+
/>
|
|
322
|
+
<button
|
|
323
|
+
onClick={handlePopulateCentroids}
|
|
324
|
+
disabled={centroidsStatus?.isPopulating || isPopulating}
|
|
325
|
+
className={classNames(
|
|
326
|
+
"flex items-center gap-2 rounded px-3 py-1.5 text-sm font-medium transition-colors",
|
|
327
|
+
{
|
|
328
|
+
"bg-purple-500 text-white hover:bg-purple-600":
|
|
329
|
+
!centroidsStatus?.isPopulating && !isPopulating,
|
|
330
|
+
"cursor-not-allowed bg-gray-300 text-gray-500":
|
|
331
|
+
centroidsStatus?.isPopulating || isPopulating,
|
|
332
|
+
},
|
|
333
|
+
)}
|
|
334
|
+
>
|
|
335
|
+
<Database className="h-4 w-4" strokeWidth={1} />
|
|
336
|
+
{centroidsStatus?.isPopulating || isPopulating
|
|
337
|
+
? centroidsStatus?.status || "Populating..."
|
|
338
|
+
: "Populate Centroids"}
|
|
339
|
+
</button>
|
|
340
|
+
<button
|
|
341
|
+
onClick={handleCleanup}
|
|
342
|
+
disabled={isCleaning}
|
|
343
|
+
className={classNames(
|
|
344
|
+
"flex items-center gap-2 rounded px-3 py-1.5 text-sm font-medium transition-colors",
|
|
345
|
+
{
|
|
346
|
+
"bg-gray-100 text-gray-700 hover:bg-gray-200": !isCleaning,
|
|
347
|
+
"cursor-not-allowed bg-gray-300 text-gray-500": isCleaning,
|
|
348
|
+
},
|
|
349
|
+
)}
|
|
350
|
+
>
|
|
351
|
+
<Trash2 className="h-4 w-4" strokeWidth={1} />
|
|
352
|
+
{isCleaning ? "Cleaning..." : "Cleanup"}
|
|
353
|
+
</button>
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
31
356
|
</div>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
357
|
+
|
|
358
|
+
{error && (
|
|
359
|
+
<div className="rounded border border-red-200 bg-red-50 p-3 text-sm text-red-700">
|
|
360
|
+
{error}
|
|
361
|
+
</div>
|
|
362
|
+
)}
|
|
363
|
+
|
|
364
|
+
{isLoading && !indexStatus ? (
|
|
365
|
+
<div className="flex items-center justify-center py-8">
|
|
366
|
+
<RefreshCw
|
|
367
|
+
className="h-6 w-6 animate-spin text-gray-400"
|
|
368
|
+
strokeWidth={1}
|
|
369
|
+
/>
|
|
370
|
+
</div>
|
|
371
|
+
) : indexStatus ? (
|
|
372
|
+
<div className="space-y-4">
|
|
373
|
+
<div className="grid grid-cols-2 gap-4">
|
|
374
|
+
<div className="rounded bg-gray-50 p-3">
|
|
375
|
+
<div className="text-sm text-gray-600">Indexed Items</div>
|
|
376
|
+
<div className="text-2xl font-semibold">
|
|
377
|
+
{indexStatus.numberOfIndexedItems?.toLocaleString() ?? "0"}
|
|
378
|
+
</div>
|
|
379
|
+
</div>
|
|
380
|
+
<div className="rounded bg-gray-50 p-3">
|
|
381
|
+
<div className="text-sm text-gray-600">Status</div>
|
|
382
|
+
{(() => {
|
|
383
|
+
const batches = indexStatus?.batches || [];
|
|
384
|
+
const inProgress = batches.some((b) => {
|
|
385
|
+
const s = (b.status || "").toLowerCase();
|
|
386
|
+
return (
|
|
387
|
+
s === "submitted" || s === "processing" || s === "queued"
|
|
388
|
+
);
|
|
389
|
+
});
|
|
390
|
+
const statusLabel = isCollecting
|
|
391
|
+
? "Collecting items"
|
|
392
|
+
: isSubmitting || inProgress
|
|
393
|
+
? "Generating Embeddings"
|
|
394
|
+
: importStatus?.isImporting
|
|
395
|
+
? importStatus.status || "Importing embeddings"
|
|
396
|
+
: centroidsStatus?.isPopulating
|
|
397
|
+
? centroidsStatus.status || "Populating centroids"
|
|
398
|
+
: indexStatus.rebuilding
|
|
399
|
+
? "Rebuilding"
|
|
400
|
+
: "Ready";
|
|
401
|
+
const statusClass =
|
|
402
|
+
statusLabel === "Rebuilding"
|
|
403
|
+
? "text-orange-600"
|
|
404
|
+
: statusLabel === "Generating Embeddings"
|
|
405
|
+
? "text-blue-600"
|
|
406
|
+
: statusLabel === "Collecting items"
|
|
407
|
+
? "text-indigo-600"
|
|
408
|
+
: importStatus?.isImporting
|
|
409
|
+
? "text-purple-600"
|
|
410
|
+
: centroidsStatus?.isPopulating
|
|
411
|
+
? "text-pink-600"
|
|
412
|
+
: "text-green-600";
|
|
413
|
+
return (
|
|
414
|
+
<div
|
|
415
|
+
className={classNames("text-sm font-medium", statusClass)}
|
|
416
|
+
>
|
|
417
|
+
{statusLabel}
|
|
418
|
+
</div>
|
|
419
|
+
);
|
|
420
|
+
})()}
|
|
421
|
+
</div>
|
|
422
|
+
</div>
|
|
423
|
+
|
|
424
|
+
{(() => {
|
|
425
|
+
const stagedCount = staging?.fileCount ?? 0;
|
|
426
|
+
|
|
427
|
+
// Only show staging section if there are staged files
|
|
428
|
+
if (stagedCount > 0) {
|
|
429
|
+
return (
|
|
430
|
+
<div className="rounded bg-gray-50 p-3">
|
|
431
|
+
<div className="flex items-center justify-between text-sm text-gray-600">
|
|
432
|
+
<span>Staged Files</span>
|
|
433
|
+
<span>{stagedCount}</span>
|
|
434
|
+
</div>
|
|
435
|
+
<div className="mt-1 flex items-center justify-between text-sm text-gray-600">
|
|
436
|
+
<span>Estimated Input Tokens</span>
|
|
437
|
+
<span>
|
|
438
|
+
{(staging?.estimatedInputTokens ?? 0).toLocaleString()}
|
|
439
|
+
</span>
|
|
440
|
+
</div>
|
|
441
|
+
<div className="mt-1 flex items-center justify-between text-sm text-gray-600">
|
|
442
|
+
<span>Estimated Batch Cost</span>
|
|
443
|
+
<span>
|
|
444
|
+
~$
|
|
445
|
+
{(
|
|
446
|
+
staging?.estimatedBatchCost ??
|
|
447
|
+
staging?.estimatedCost ??
|
|
448
|
+
0
|
|
449
|
+
).toFixed(2)}
|
|
450
|
+
</span>
|
|
451
|
+
</div>
|
|
452
|
+
<div className="mt-1 flex items-center justify-between text-sm text-gray-600">
|
|
453
|
+
<span>Estimated Direct Cost</span>
|
|
454
|
+
<span>
|
|
455
|
+
~${(staging?.estimatedDirectCost ?? 0).toFixed(2)}
|
|
456
|
+
</span>
|
|
457
|
+
</div>
|
|
458
|
+
<div className="mt-2 text-xs text-gray-700">
|
|
459
|
+
Next step:{" "}
|
|
460
|
+
<span className="font-medium">
|
|
461
|
+
Choose: Batch or Direct generation
|
|
462
|
+
</span>
|
|
463
|
+
</div>
|
|
464
|
+
</div>
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Show next step info even when no staged files (but not "Rebuild Index")
|
|
469
|
+
const batches = indexStatus?.batches || [];
|
|
470
|
+
const completedCount = batches.filter(
|
|
471
|
+
(b) => (b.status || "").toLowerCase() === "completed",
|
|
472
|
+
).length;
|
|
473
|
+
const inProgress = batches.some((b) => {
|
|
474
|
+
const s = (b.status || "").toLowerCase();
|
|
475
|
+
return (
|
|
476
|
+
s === "submitted" || s === "processing" || s === "queued"
|
|
477
|
+
);
|
|
478
|
+
});
|
|
479
|
+
const nextStep =
|
|
480
|
+
isCollecting ||
|
|
481
|
+
isSubmitting ||
|
|
482
|
+
isStartingDirect ||
|
|
483
|
+
indexStatus?.rebuilding ||
|
|
484
|
+
importStatus?.isImporting ||
|
|
485
|
+
centroidsStatus?.isPopulating
|
|
486
|
+
? ""
|
|
487
|
+
: inProgress
|
|
488
|
+
? "Generating Embeddings"
|
|
489
|
+
: completedCount > 0
|
|
490
|
+
? "Import embeddings"
|
|
491
|
+
: ""; // Don't show "Rebuild Index" as next step
|
|
492
|
+
|
|
493
|
+
if (nextStep) {
|
|
494
|
+
return (
|
|
495
|
+
<div className="rounded bg-gray-50 p-3">
|
|
496
|
+
<div className="text-xs text-gray-700">
|
|
497
|
+
Next step: <span className="font-medium">{nextStep}</span>
|
|
498
|
+
</div>
|
|
499
|
+
</div>
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return null;
|
|
504
|
+
})()}
|
|
505
|
+
|
|
506
|
+
{indexStatus.batches && indexStatus.batches.length > 0 && (
|
|
507
|
+
<div>
|
|
508
|
+
<h3 className="mb-2 text-sm font-medium text-gray-700">
|
|
509
|
+
Recent Batches
|
|
510
|
+
</h3>
|
|
511
|
+
<div className="space-y-2 overflow-y-auto">
|
|
512
|
+
{indexStatus.batches.map((batch) => (
|
|
513
|
+
<div
|
|
514
|
+
key={batch.id}
|
|
515
|
+
className="rounded bg-gray-50 p-2 text-sm"
|
|
516
|
+
>
|
|
517
|
+
<div className="flex items-center justify-between">
|
|
518
|
+
<div className="flex items-center gap-2">
|
|
519
|
+
{getBatchStatusIcon(batch.status)}
|
|
520
|
+
<span
|
|
521
|
+
className={`rounded px-2 py-0.5 text-xs font-medium ${getBatchStatusLabelStyles(batch.status)}`}
|
|
522
|
+
>
|
|
523
|
+
{batch.status ?? "Unknown"}
|
|
524
|
+
</span>
|
|
525
|
+
<span className="font-mono text-xs">
|
|
526
|
+
{batch.id?.slice(-8) ?? "Unknown"}
|
|
527
|
+
</span>
|
|
528
|
+
</div>
|
|
529
|
+
<div className="text-right">
|
|
530
|
+
<div className="text-gray-600">
|
|
531
|
+
{batch.numberOfItems ?? 0} items
|
|
532
|
+
</div>
|
|
533
|
+
<div className="text-xs text-gray-500">
|
|
534
|
+
{batch.created
|
|
535
|
+
? new Date(batch.created).toLocaleDateString()
|
|
536
|
+
: "Unknown"}
|
|
537
|
+
</div>
|
|
538
|
+
</div>
|
|
539
|
+
</div>
|
|
540
|
+
{((batch.firstErrors && batch.firstErrors.length > 0) ||
|
|
541
|
+
batch.errorMessage) && (
|
|
542
|
+
<div className="mt-1 text-xs text-red-600">
|
|
543
|
+
{(() => {
|
|
544
|
+
const messages =
|
|
545
|
+
batch.firstErrors && batch.firstErrors.length > 0
|
|
546
|
+
? batch.firstErrors.slice(0, 3)
|
|
547
|
+
: batch.errorMessage
|
|
548
|
+
? [batch.errorMessage]
|
|
549
|
+
: [];
|
|
550
|
+
const shownCount = messages.length;
|
|
551
|
+
const totalCount =
|
|
552
|
+
batch.errorCount && batch.errorCount > 0
|
|
553
|
+
? batch.errorCount
|
|
554
|
+
: shownCount;
|
|
555
|
+
const suffix =
|
|
556
|
+
totalCount > shownCount
|
|
557
|
+
? ` (+${totalCount - shownCount} more)`
|
|
558
|
+
: "";
|
|
559
|
+
return `${messages.join(" · ")}${suffix}`;
|
|
560
|
+
})()}
|
|
561
|
+
</div>
|
|
562
|
+
)}
|
|
563
|
+
</div>
|
|
564
|
+
))}
|
|
565
|
+
</div>
|
|
566
|
+
</div>
|
|
39
567
|
)}
|
|
40
|
-
|
|
41
|
-
{
|
|
568
|
+
|
|
569
|
+
{indexStatus.latestEmbeddedItems &&
|
|
570
|
+
indexStatus.latestEmbeddedItems.length > 0 && (
|
|
571
|
+
<div>
|
|
572
|
+
<h3 className="mb-2 text-sm font-medium text-gray-700">
|
|
573
|
+
Latest Embeddings Generated
|
|
574
|
+
</h3>
|
|
575
|
+
<div className="max-h-72 space-y-1 overflow-y-auto">
|
|
576
|
+
{indexStatus.latestEmbeddedItems.slice(0, 50).map((it) => (
|
|
577
|
+
<div
|
|
578
|
+
key={it.itemId + it.created}
|
|
579
|
+
className="flex items-center justify-between rounded bg-gray-50 p-2 text-sm"
|
|
580
|
+
>
|
|
581
|
+
<div className="min-w-0">
|
|
582
|
+
<div className="truncate font-medium">
|
|
583
|
+
{it.name || it.itemId.slice(-8)}
|
|
584
|
+
</div>
|
|
585
|
+
{it.path && (
|
|
586
|
+
<div className="truncate text-xs text-gray-500">
|
|
587
|
+
{it.path}
|
|
588
|
+
</div>
|
|
589
|
+
)}
|
|
590
|
+
</div>
|
|
591
|
+
<div className="ml-2 shrink-0 text-xs text-gray-500">
|
|
592
|
+
{new Date(it.created).toLocaleString()}
|
|
593
|
+
</div>
|
|
594
|
+
</div>
|
|
595
|
+
))}
|
|
596
|
+
</div>
|
|
597
|
+
</div>
|
|
598
|
+
)}
|
|
42
599
|
</div>
|
|
43
|
-
)
|
|
44
|
-
</div>
|
|
45
|
-
<div className="flex-1 p-2">
|
|
46
|
-
{selectedIndex && <IndexSettings index={{ name: selectedIndex }} />}
|
|
600
|
+
) : null}
|
|
47
601
|
</div>
|
|
48
602
|
</div>
|
|
49
603
|
);
|
|
@@ -91,7 +91,7 @@ export function Status() {
|
|
|
91
91
|
name: "content",
|
|
92
92
|
defaultSize: "auto",
|
|
93
93
|
content: (
|
|
94
|
-
<div className="absolute inset-0 overflow-auto">
|
|
94
|
+
<div className="absolute inset-0 overflow-auto rounded-xl bg-white">
|
|
95
95
|
{selectedPanel ? (
|
|
96
96
|
selectedPanel.content
|
|
97
97
|
) : (
|
|
@@ -34,6 +34,7 @@ export function CheckboxEditor({
|
|
|
34
34
|
key={fieldItem.id + field.id + fieldItem.language + fieldItem.version}
|
|
35
35
|
checked={field.value}
|
|
36
36
|
disabled={readOnly}
|
|
37
|
+
className="focus-shadow"
|
|
37
38
|
onCheckedChange={(checked) => {
|
|
38
39
|
setIsUpdating(true);
|
|
39
40
|
editContext.operations.editField({
|
|
@@ -2,7 +2,8 @@ import { useEffect, useState, useRef } from "react";
|
|
|
2
2
|
|
|
3
3
|
import { useDebouncedCallback } from "use-debounce";
|
|
4
4
|
import { createEditorAiContext } from "../ai/editorAiContext";
|
|
5
|
-
import { executePrompt
|
|
5
|
+
import { executePrompt } from "../services/aiService";
|
|
6
|
+
import { executeSearch } from "../services/searchService";
|
|
6
7
|
import { useEditContext } from "../client/editContext";
|
|
7
8
|
import { ProgressSpinner } from "primereact/progressspinner";
|
|
8
9
|
import { InputText } from "primereact/inputtext";
|
|
@@ -7,7 +7,7 @@ import { ItemTreeNodeData, getChildren } from "../services/contentService";
|
|
|
7
7
|
import { useEditContext } from "../client/editContext";
|
|
8
8
|
import { Button } from "../../components/ui/button";
|
|
9
9
|
import { UploadButton } from "../../components/ui/upload-button";
|
|
10
|
-
import { executeSearch } from "../services/
|
|
10
|
+
import { executeSearch } from "../services/searchService";
|
|
11
11
|
import { ClockIcon, Upload } from "lucide-react";
|
|
12
12
|
|
|
13
13
|
import DialogButtons from "../ui/DialogButtons";
|
|
@@ -11,7 +11,7 @@ import { ResultItem } from "../ui/ItemSearch";
|
|
|
11
11
|
import { ScrollingContentTree } from "../ScrollingContentTree";
|
|
12
12
|
import { ItemTreeNodeData } from "../services/contentService";
|
|
13
13
|
import { SimpleIconButton } from "../ui/SimpleIconButton";
|
|
14
|
-
import { executeSearch } from "../services/
|
|
14
|
+
import { executeSearch } from "../services/searchService";
|
|
15
15
|
import { ItemList } from "../ui/ItemList";
|
|
16
16
|
import { DotsVerticalIcon } from "@radix-ui/react-icons";
|
|
17
17
|
import {
|