@alpaca-editor/core 1.0.4059 → 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.
Files changed (55) hide show
  1. package/dist/components/ui/checkbox.js +1 -1
  2. package/dist/components/ui/checkbox.js.map +1 -1
  3. package/dist/components/ui/tooltip.js +2 -2
  4. package/dist/components/ui/tooltip.js.map +1 -1
  5. package/dist/editor/FieldListField.js +2 -1
  6. package/dist/editor/FieldListField.js.map +1 -1
  7. package/dist/editor/control-center/IndexOverview.js +85 -41
  8. package/dist/editor/control-center/IndexOverview.js.map +1 -1
  9. package/dist/editor/field-types/CheckboxEditor.js +1 -1
  10. package/dist/editor/field-types/CheckboxEditor.js.map +1 -1
  11. package/dist/editor/page-editor-chrome/useInlineAICompletion.js +1 -1
  12. package/dist/editor/page-editor-chrome/useInlineAICompletion.js.map +1 -1
  13. package/dist/editor/reviews/Comment.js +63 -108
  14. package/dist/editor/reviews/Comment.js.map +1 -1
  15. package/dist/editor/reviews/CommentDisplayPopover.js +55 -49
  16. package/dist/editor/reviews/CommentDisplayPopover.js.map +1 -1
  17. package/dist/editor/reviews/CommentEditor.d.ts +20 -0
  18. package/dist/editor/reviews/CommentEditor.js +57 -0
  19. package/dist/editor/reviews/CommentEditor.js.map +1 -0
  20. package/dist/editor/reviews/CommentPopover.js +11 -58
  21. package/dist/editor/reviews/CommentPopover.js.map +1 -1
  22. package/dist/editor/reviews/CommentView.d.ts +19 -0
  23. package/dist/editor/reviews/CommentView.js +46 -0
  24. package/dist/editor/reviews/CommentView.js.map +1 -0
  25. package/dist/editor/services/indexService.d.ts +3 -1
  26. package/dist/editor/services/indexService.js +7 -0
  27. package/dist/editor/services/indexService.js.map +1 -1
  28. package/dist/editor/sidebar/ViewSelector.js +7 -6
  29. package/dist/editor/sidebar/ViewSelector.js.map +1 -1
  30. package/dist/editor/ui/CenteredMessage.js +1 -1
  31. package/dist/editor/ui/CenteredMessage.js.map +1 -1
  32. package/dist/editor/ui/SimpleToolbar.js +1 -1
  33. package/dist/editor/ui/SimpleToolbar.js.map +1 -1
  34. package/dist/revision.d.ts +2 -2
  35. package/dist/revision.js +2 -2
  36. package/dist/styles.css +19 -5
  37. package/dist/types.d.ts +7 -0
  38. package/package.json +1 -1
  39. package/src/components/ui/checkbox.tsx +1 -1
  40. package/src/components/ui/tooltip.tsx +5 -3
  41. package/src/editor/FieldListField.tsx +40 -23
  42. package/src/editor/control-center/IndexOverview.tsx +168 -79
  43. package/src/editor/field-types/CheckboxEditor.tsx +1 -0
  44. package/src/editor/page-editor-chrome/useInlineAICompletion.tsx +1 -1
  45. package/src/editor/reviews/Comment.tsx +97 -275
  46. package/src/editor/reviews/CommentDisplayPopover.tsx +90 -219
  47. package/src/editor/reviews/CommentEditor.tsx +153 -0
  48. package/src/editor/reviews/CommentPopover.tsx +17 -108
  49. package/src/editor/reviews/CommentView.tsx +229 -0
  50. package/src/editor/services/indexService.ts +12 -0
  51. package/src/editor/sidebar/ViewSelector.tsx +40 -27
  52. package/src/editor/ui/CenteredMessage.tsx +1 -1
  53. package/src/editor/ui/SimpleToolbar.tsx +1 -1
  54. package/src/revision.ts +2 -2
  55. package/src/types.ts +8 -0
@@ -22,7 +22,7 @@ import { FieldHistory } from "./FieldHistory";
22
22
  import {
23
23
  History,
24
24
  MessageCircle,
25
- WandSparkles,
25
+ WandSparkles,
26
26
  ExternalLink,
27
27
  } from "lucide-react";
28
28
  import { Checkbox } from "../components/ui/checkbox";
@@ -32,6 +32,11 @@ import {
32
32
  PopoverContent,
33
33
  PopoverTrigger,
34
34
  } from "../components/ui/popover";
35
+ import {
36
+ Tooltip as InternalTooltip,
37
+ TooltipContent,
38
+ TooltipTrigger,
39
+ } from "../components/ui/tooltip";
35
40
 
36
41
  export default function FieldListField({
37
42
  field,
@@ -60,9 +65,9 @@ export default function FieldListField({
60
65
  // Field-specific subscription - only rerenders when THIS field changes
61
66
  const { isModified, isSaved } = useFieldModification(
62
67
  field.id,
63
- fieldItem.id,
68
+ fieldItem.id,
64
69
  fieldItem.language,
65
- fieldItem.version
70
+ fieldItem.version,
66
71
  );
67
72
 
68
73
  const [showRawValue, setShowRawValue] = useState(false);
@@ -344,7 +349,10 @@ export default function FieldListField({
344
349
  {!editContext.configuration.forceFullscreen && (
345
350
  <SimpleIconButton
346
351
  icon={
347
- <MessageCircle strokeWidth={1} className="h-3.5 w-3.5" />
352
+ <MessageCircle
353
+ strokeWidth={1}
354
+ className="h-3.5 w-3.5"
355
+ />
348
356
  }
349
357
  onClick={() => {
350
358
  editContext.switchView("comments");
@@ -354,26 +362,35 @@ export default function FieldListField({
354
362
  />
355
363
  )}
356
364
  {!readonly && renderGeneratorButtons}
357
- <Popover
358
- open={fieldHistoryOpen}
359
- onOpenChange={setFieldHistoryOpen}
360
- >
361
- <PopoverTrigger asChild>
362
- <button
363
- className="rounded p-1 transition-colors hover:bg-gray-100"
364
- aria-label="Field History"
365
+ <InternalTooltip>
366
+ <Popover
367
+ open={fieldHistoryOpen}
368
+ onOpenChange={setFieldHistoryOpen}
369
+ >
370
+ <PopoverTrigger asChild>
371
+ <TooltipTrigger asChild>
372
+ <button
373
+ className="rounded p-1 transition-colors hover:bg-gray-100"
374
+ aria-label="Field History"
375
+ >
376
+ <History strokeWidth={1} className="h-3.5 w-3.5" />
377
+ </button>
378
+ </TooltipTrigger>
379
+ </PopoverTrigger>
380
+ <PopoverContent
381
+ className="w-80 p-2"
382
+ align="end"
383
+ side="top"
365
384
  >
366
- <History strokeWidth={1} className="h-3.5 w-3.5" />
367
- </button>
368
- </PopoverTrigger>
369
- <PopoverContent className="w-80 p-2" align="end" side="top">
370
- <FieldHistory
371
- field={field}
372
- onHover={(x) => setHistoryEntry(x)}
373
- onRevert={() => setFieldHistoryOpen(false)}
374
- />
375
- </PopoverContent>
376
- </Popover>
385
+ <FieldHistory
386
+ field={field}
387
+ onHover={(x) => setHistoryEntry(x)}
388
+ onRevert={() => setFieldHistoryOpen(false)}
389
+ />
390
+ </PopoverContent>
391
+ </Popover>
392
+ <TooltipContent>Field History</TooltipContent>
393
+ </InternalTooltip>
377
394
  </div>
378
395
  </>
379
396
  )}
@@ -9,7 +9,12 @@ import {
9
9
  } from "lucide-react";
10
10
 
11
11
  import { classNames } from "primereact/utils";
12
- import { IndexStatus, StagingStatus, ImportStatus } from "../../types";
12
+ import {
13
+ IndexStatus,
14
+ StagingStatus,
15
+ ImportStatus,
16
+ CentroidsStatus,
17
+ } from "../../types";
13
18
  import {
14
19
  getIndexStatus,
15
20
  cleanupBatches,
@@ -19,6 +24,8 @@ import {
19
24
  submitStaged,
20
25
  getStagingStatus,
21
26
  getImportStatus,
27
+ getCentroidsStatus,
28
+ startDirectGeneration,
22
29
  } from "../services/indexService";
23
30
 
24
31
  export function IndexOverview() {
@@ -31,8 +38,11 @@ export function IndexOverview() {
31
38
  const [isFinalizing, setIsFinalizing] = useState(false);
32
39
  const [staging, setStaging] = useState<StagingStatus | null>(null);
33
40
  const [importStatus, setImportStatus] = useState<ImportStatus | null>(null);
41
+ const [centroidsStatus, setCentroidsStatus] =
42
+ useState<CentroidsStatus | null>(null);
34
43
  const [isCollecting, setIsCollecting] = useState(false);
35
44
  const [isSubmitting, setIsSubmitting] = useState(false);
45
+ const [isStartingDirect, setIsStartingDirect] = useState(false);
36
46
 
37
47
  const fetchStatus = async () => {
38
48
  try {
@@ -44,6 +54,9 @@ export function IndexOverview() {
44
54
  setStaging(st || null);
45
55
  const importSt = (await getImportStatus()) as ImportStatus | null;
46
56
  setImportStatus(importSt || null);
57
+ const centroidsSt =
58
+ (await getCentroidsStatus()) as CentroidsStatus | null;
59
+ setCentroidsStatus(centroidsSt || null);
47
60
  } catch (err) {
48
61
  setError(
49
62
  err instanceof Error ? err.message : "Failed to fetch index status",
@@ -83,6 +96,23 @@ export function IndexOverview() {
83
96
  }
84
97
  };
85
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
+
86
116
  const handleCleanup = async () => {
87
117
  try {
88
118
  setIsCleaning(true);
@@ -168,7 +198,7 @@ export function IndexOverview() {
168
198
  return (
169
199
  <div className="flex h-full items-stretch">
170
200
  <div className="flex min-w-96 flex-col gap-4 p-4">
171
- <div className="flex items-center justify-between border-b border-gray-200 pb-2">
201
+ <div className="flex items-center justify-between gap-4 border-b border-gray-200 pb-2">
172
202
  <div className="flex items-center gap-2">
173
203
  <Database className="h-5 w-5" strokeWidth={1} />
174
204
  <span className="font-semibold">Search Index</span>
@@ -191,29 +221,53 @@ export function IndexOverview() {
191
221
  isSubmitting ||
192
222
  inProgressCount > 0 ||
193
223
  indexStatus?.rebuilding ||
194
- importStatus?.isImporting
224
+ importStatus?.isImporting ||
225
+ centroidsStatus?.isPopulating
195
226
  ) {
196
227
  return null;
197
228
  } else if (stagedCount > 0) {
229
+ const batchCost =
230
+ staging?.estimatedBatchCost ?? staging?.estimatedCost ?? 0;
231
+ const directCost = staging?.estimatedDirectCost ?? 0;
198
232
  return (
199
- <button
200
- onClick={handleSubmitStaged}
201
- disabled={isSubmitting || stagedCount === 0}
202
- className={classNames(
203
- "flex items-center gap-2 rounded px-3 py-1.5 text-sm font-medium transition-colors",
204
- {
205
- "bg-amber-600 text-white hover:bg-amber-700":
206
- !isSubmitting && stagedCount > 0,
207
- "cursor-not-allowed bg-gray-300 text-gray-500":
208
- isSubmitting || stagedCount === 0,
209
- },
210
- )}
211
- >
212
- <CheckCircle className="h-4 w-4" strokeWidth={1} />
213
- {isSubmitting
214
- ? "Submitting..."
215
- : "Submit and Generate Embeddings"}
216
- </button>
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>
217
271
  );
218
272
  } else if (completedCount > 0) {
219
273
  return (
@@ -250,7 +304,7 @@ export function IndexOverview() {
250
304
  )}
251
305
  >
252
306
  <RefreshCw className="h-4 w-4" strokeWidth={1} />
253
- {isCollecting ? "Collecting..." : "Collect items"}
307
+ {isCollecting ? "Collecting..." : "Rebuild Index"}
254
308
  </button>
255
309
  );
256
310
  })()}
@@ -267,19 +321,21 @@ export function IndexOverview() {
267
321
  />
268
322
  <button
269
323
  onClick={handlePopulateCentroids}
270
- disabled={isPopulating}
324
+ disabled={centroidsStatus?.isPopulating || isPopulating}
271
325
  className={classNames(
272
326
  "flex items-center gap-2 rounded px-3 py-1.5 text-sm font-medium transition-colors",
273
327
  {
274
328
  "bg-purple-500 text-white hover:bg-purple-600":
275
- !isPopulating,
329
+ !centroidsStatus?.isPopulating && !isPopulating,
276
330
  "cursor-not-allowed bg-gray-300 text-gray-500":
277
- isPopulating,
331
+ centroidsStatus?.isPopulating || isPopulating,
278
332
  },
279
333
  )}
280
334
  >
281
335
  <Database className="h-4 w-4" strokeWidth={1} />
282
- {isPopulating ? "Populating..." : "Populate Centroids"}
336
+ {centroidsStatus?.isPopulating || isPopulating
337
+ ? centroidsStatus?.status || "Populating..."
338
+ : "Populate Centroids"}
283
339
  </button>
284
340
  <button
285
341
  onClick={handleCleanup}
@@ -337,9 +393,11 @@ export function IndexOverview() {
337
393
  ? "Generating Embeddings"
338
394
  : importStatus?.isImporting
339
395
  ? importStatus.status || "Importing embeddings"
340
- : indexStatus.rebuilding
341
- ? "Rebuilding"
342
- : "Ready";
396
+ : centroidsStatus?.isPopulating
397
+ ? centroidsStatus.status || "Populating centroids"
398
+ : indexStatus.rebuilding
399
+ ? "Rebuilding"
400
+ : "Ready";
343
401
  const statusClass =
344
402
  statusLabel === "Rebuilding"
345
403
  ? "text-orange-600"
@@ -349,7 +407,9 @@ export function IndexOverview() {
349
407
  ? "text-indigo-600"
350
408
  : importStatus?.isImporting
351
409
  ? "text-purple-600"
352
- : "text-green-600";
410
+ : centroidsStatus?.isPopulating
411
+ ? "text-pink-600"
412
+ : "text-green-600";
353
413
  return (
354
414
  <div
355
415
  className={classNames("text-sm font-medium", statusClass)}
@@ -361,58 +421,87 @@ export function IndexOverview() {
361
421
  </div>
362
422
  </div>
363
423
 
364
- <div className="rounded bg-gray-50 p-3">
365
- <div className="flex items-center justify-between text-sm text-gray-600">
366
- <span>Staged Files</span>
367
- <span>{staging?.fileCount ?? 0}</span>
368
- </div>
369
- <div className="mt-1 flex items-center justify-between text-sm text-gray-600">
370
- <span>Estimated Input Tokens</span>
371
- <span>
372
- {(staging?.estimatedInputTokens ?? 0).toLocaleString()}
373
- </span>
374
- </div>
375
- <div className="mt-1 flex items-center justify-between text-sm text-gray-600">
376
- <span>Estimated Cost</span>
377
- <span>~${(staging?.estimatedCost ?? 0).toFixed(2)}</span>
378
- </div>
379
- {(() => {
380
- const stagedCount = staging?.fileCount ?? 0;
381
- const batches = indexStatus?.batches || [];
382
- const completedCount = batches.filter(
383
- (b) => (b.status || "").toLowerCase() === "completed",
384
- ).length;
385
- const inProgress = batches.some((b) => {
386
- const s = (b.status || "").toLowerCase();
387
- return (
388
- s === "submitted" || s === "processing" || s === "queued"
389
- );
390
- });
391
- const nextStep =
392
- isCollecting ||
393
- isSubmitting ||
394
- indexStatus?.rebuilding ||
395
- importStatus?.isImporting
396
- ? ""
397
- : stagedCount > 0
398
- ? "Submit and Generate Embeddings"
399
- : inProgress
400
- ? "Generating Embeddings"
401
- : completedCount > 0
402
- ? "Import embeddings"
403
- : "Collect items";
424
+ {(() => {
425
+ const stagedCount = staging?.fileCount ?? 0;
426
+
427
+ // Only show staging section if there are staged files
428
+ if (stagedCount > 0) {
404
429
  return (
405
- <div className="mt-2 text-xs text-gray-700">
406
- {nextStep && (
407
- <>
408
- Next step:{" "}
409
- <span className="font-medium">{nextStep}</span>
410
- </>
411
- )}
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>
412
464
  </div>
413
465
  );
414
- })()}
415
- </div>
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
+ })()}
416
505
 
417
506
  {indexStatus.batches && indexStatus.batches.length > 0 && (
418
507
  <div>
@@ -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({
@@ -375,7 +375,7 @@ ONLY provide the completion text (what comes after the user's text), never repea
375
375
  content: contentUpToCursor,
376
376
  },
377
377
  },
378
- { model: "gpt-4.1-nano" },
378
+ { model: "gpt-4.1-nano", sessionId: editContext.sessionId },
379
379
  { signal },
380
380
  );
381
381