strapi-content-embeddings 0.1.4 → 0.1.6

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.
@@ -2,10 +2,10 @@ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import { useFetchClient, Layouts, useNotification, Page } from "@strapi/strapi/admin";
3
3
  import { Link, useNavigate, NavLink, useParams, Routes, Route } from "react-router-dom";
4
4
  import { useRef, useState, useEffect, useCallback, useMemo } from "react";
5
- import { EmptyStateLayout, Button, Tr, Box, Table, Thead, Th, Typography, VisuallyHidden, Tbody, Td, Flex, IconButton, Modal, TextInput, Link as Link$1, Accordion, Main, Loader, Field, Textarea, Grid, Dialog } from "@strapi/design-system";
5
+ import { EmptyStateLayout, Button, Tr, Box, Table, Thead, Th, Typography, VisuallyHidden, Tbody, Td, Flex, IconButton, Modal, TextInput, Link as Link$1, Accordion, Main, Loader, Pagination, PreviousLink, PageLink, NextLink, Field, Textarea, Badge, Grid, Dialog } from "@strapi/design-system";
6
6
  import { Plus, ArrowRight, Search, ArrowLeft, Cross, Check, Pencil, Trash } from "@strapi/icons";
7
7
  import qs from "qs";
8
- import { P as PLUGIN_ID, R as RobotIcon, M as MarkdownEditor } from "./index-BWSiu_nE.mjs";
8
+ import { P as PLUGIN_ID, R as RobotIcon, M as MarkdownEditor } from "./index-CIpGvEcJ.mjs";
9
9
  import styled from "styled-components";
10
10
  import ReactMarkdown from "react-markdown";
11
11
  import { useIntl } from "react-intl";
@@ -98,7 +98,7 @@ function EmbeddingsTable({ data }) {
98
98
  const handleRowClick = (documentId) => {
99
99
  navigate(`/plugins/${PLUGIN_ID}/embeddings/${documentId}`);
100
100
  };
101
- return /* @__PURE__ */ jsx(Box, { padding: 8, background: "neutral100", children: /* @__PURE__ */ jsxs(Table, { colCount: 5, rowCount: data.length + 1, children: [
101
+ return /* @__PURE__ */ jsx(Box, { padding: 0, background: "neutral100", children: /* @__PURE__ */ jsxs(Table, { colCount: 5, rowCount: data.length + 1, children: [
102
102
  /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
103
103
  /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "ID" }) }),
104
104
  /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Title" }) }),
@@ -462,6 +462,7 @@ function ChatModal() {
462
462
  ] }) })
463
463
  ] });
464
464
  }
465
+ const PAGE_SIZE = 10;
465
466
  function debounce(func, wait) {
466
467
  let timeout;
467
468
  return (...args) => {
@@ -475,21 +476,20 @@ function HomePage() {
475
476
  const [embeddings, setEmbeddings] = useState(null);
476
477
  const [search, setSearch] = useState("");
477
478
  const [isLoading, setIsLoading] = useState(true);
478
- const buildQuery = (searchTerm) => qs.stringify({
479
+ const [currentPage, setCurrentPage] = useState(1);
480
+ const totalPages = embeddings ? Math.ceil(embeddings.totalCount / PAGE_SIZE) : 0;
481
+ const buildQuery = (searchTerm, page) => qs.stringify({
482
+ page,
483
+ pageSize: PAGE_SIZE,
479
484
  filters: searchTerm ? {
480
- $or: [
481
- { title: { $containsi: searchTerm } },
482
- { content: { $containsi: searchTerm } }
483
- ]
485
+ $or: [{ title: { $containsi: searchTerm } }, { content: { $containsi: searchTerm } }]
484
486
  } : void 0
485
487
  });
486
488
  const fetchData = useCallback(
487
- async (searchTerm) => {
489
+ async (searchTerm, page) => {
488
490
  setIsLoading(true);
489
491
  try {
490
- const response = await get(
491
- `/${PLUGIN_ID}/embeddings/find?${buildQuery(searchTerm)}`
492
- );
492
+ const response = await get(`/${PLUGIN_ID}/embeddings/find?${buildQuery(searchTerm, page)}`);
493
493
  setEmbeddings(response.data);
494
494
  } catch (error) {
495
495
  console.error("Failed to fetch embeddings:", error);
@@ -500,13 +500,13 @@ function HomePage() {
500
500
  },
501
501
  [get]
502
502
  );
503
- const debouncedFetch = useMemo(
504
- () => debounce(fetchData, 500),
505
- [fetchData]
506
- );
503
+ const debouncedFetch = useMemo(() => debounce(fetchData, 500), [fetchData]);
504
+ useEffect(() => {
505
+ debouncedFetch(search, currentPage);
506
+ }, [search, currentPage, debouncedFetch]);
507
507
  useEffect(() => {
508
- debouncedFetch(search);
509
- }, [search, debouncedFetch]);
508
+ setCurrentPage(1);
509
+ }, [search]);
510
510
  const handleSearchChange = (e) => {
511
511
  setSearch(e.target.value);
512
512
  };
@@ -515,36 +515,97 @@ function HomePage() {
515
515
  };
516
516
  if (isLoading && !embeddings) {
517
517
  return /* @__PURE__ */ jsxs(Main, { children: [
518
- /* @__PURE__ */ jsx(
519
- Layouts.Header,
520
- {
521
- title: "Content Embeddings",
522
- subtitle: "Manage your content embeddings"
523
- }
524
- ),
518
+ /* @__PURE__ */ jsx(Layouts.Header, { title: "Content Embeddings", subtitle: "Manage your content embeddings" }),
525
519
  /* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsx(Loader, { children: "Loading..." }) }) }),
526
520
  /* @__PURE__ */ jsx(ChatModal, {})
527
521
  ] });
528
522
  }
529
523
  if (embeddings?.totalCount === 0 && !search) {
530
524
  return /* @__PURE__ */ jsxs(Main, { children: [
531
- /* @__PURE__ */ jsx(
532
- Layouts.Header,
533
- {
534
- title: "Content Embeddings",
535
- subtitle: "Manage your content embeddings"
536
- }
537
- ),
525
+ /* @__PURE__ */ jsx(Layouts.Header, { title: "Content Embeddings", subtitle: "Manage your content embeddings" }),
538
526
  /* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsx(EmptyState, {}) }),
539
527
  /* @__PURE__ */ jsx(ChatModal, {})
540
528
  ] });
541
529
  }
530
+ const renderEmbeddingsContent = () => {
531
+ if (isLoading) {
532
+ return /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsx(Loader, { children: "Loading..." }) });
533
+ }
534
+ if (embeddings?.data && embeddings.data.length > 0) {
535
+ return /* @__PURE__ */ jsx(EmbeddingsTable, { data: embeddings.data });
536
+ }
537
+ return /* @__PURE__ */ jsxs(Box, { padding: 8, textAlign: "center", children: [
538
+ 'No embeddings found matching "',
539
+ search,
540
+ '"'
541
+ ] });
542
+ };
543
+ const shouldShowPagination = embeddings && embeddings.totalCount > 0 && totalPages > 1;
544
+ const getVisiblePages = () => {
545
+ const pages = [];
546
+ const maxVisible = 5;
547
+ let start = Math.max(1, currentPage - Math.floor(maxVisible / 2));
548
+ const end = Math.min(totalPages, start + maxVisible - 1);
549
+ if (end - start + 1 < maxVisible) {
550
+ start = Math.max(1, end - maxVisible + 1);
551
+ }
552
+ for (let i = start; i <= end; i++) {
553
+ pages.push(i);
554
+ }
555
+ return pages;
556
+ };
557
+ const renderPagination = () => {
558
+ if (!shouldShowPagination || !embeddings) {
559
+ return null;
560
+ }
561
+ const startItem = (currentPage - 1) * PAGE_SIZE + 1;
562
+ const endItem = Math.min(currentPage * PAGE_SIZE, embeddings.totalCount);
563
+ const visiblePages = getVisiblePages();
564
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "center", gap: 3, paddingTop: 6, children: [
565
+ /* @__PURE__ */ jsxs(Pagination, { activePage: currentPage, pageCount: totalPages, children: [
566
+ /* @__PURE__ */ jsx(
567
+ PreviousLink,
568
+ {
569
+ onClick: () => setCurrentPage((p) => Math.max(1, p - 1)),
570
+ disabled: currentPage === 1,
571
+ children: "Previous"
572
+ }
573
+ ),
574
+ visiblePages.map((page) => /* @__PURE__ */ jsx(
575
+ PageLink,
576
+ {
577
+ number: page,
578
+ onClick: () => setCurrentPage(page),
579
+ children: page
580
+ },
581
+ page
582
+ )),
583
+ /* @__PURE__ */ jsx(
584
+ NextLink,
585
+ {
586
+ onClick: () => setCurrentPage((p) => Math.min(totalPages, p + 1)),
587
+ disabled: currentPage === totalPages,
588
+ children: "Next"
589
+ }
590
+ )
591
+ ] }),
592
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
593
+ "Showing ",
594
+ startItem,
595
+ " to ",
596
+ endItem,
597
+ " of ",
598
+ embeddings.totalCount,
599
+ " entries"
600
+ ] })
601
+ ] });
602
+ };
542
603
  return /* @__PURE__ */ jsxs(Main, { children: [
543
604
  /* @__PURE__ */ jsx(
544
605
  Layouts.Header,
545
606
  {
546
607
  title: "Content Embeddings",
547
- subtitle: `${embeddings?.count || 0} results found`,
608
+ subtitle: `${embeddings?.totalCount || 0} embeddings total`,
548
609
  primaryAction: /* @__PURE__ */ jsx(Button, { startIcon: /* @__PURE__ */ jsx(Plus, {}), onClick: handleCreateNew, children: "Create new embedding" })
549
610
  }
550
611
  ),
@@ -559,11 +620,8 @@ function HomePage() {
559
620
  startAction: /* @__PURE__ */ jsx(Search, {})
560
621
  }
561
622
  ) }),
562
- isLoading ? /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsx(Loader, { children: "Loading..." }) }) : embeddings?.data && embeddings.data.length > 0 ? /* @__PURE__ */ jsx(EmbeddingsTable, { data: embeddings.data }) : /* @__PURE__ */ jsxs(Box, { padding: 8, textAlign: "center", children: [
563
- 'No embeddings found matching "',
564
- search,
565
- '"'
566
- ] })
623
+ renderEmbeddingsContent(),
624
+ renderPagination()
567
625
  ] }),
568
626
  /* @__PURE__ */ jsx(ChatModal, {})
569
627
  ] });
@@ -626,7 +684,7 @@ function BackLink({ to }) {
626
684
  }
627
685
  return /* @__PURE__ */ jsx(Link$1, { tag: NavLink, to: "..", relative: "path", startIcon: /* @__PURE__ */ jsx(ArrowLeft, {}), children: "Go back" });
628
686
  }
629
- const MAX_CONTENT_LENGTH = 4e3;
687
+ const CHUNK_SIZE = 4e3;
630
688
  function CreateEmbeddings() {
631
689
  const { formatMessage } = useIntl();
632
690
  const navigate = useNavigate();
@@ -636,8 +694,10 @@ function CreateEmbeddings() {
636
694
  const [content, setContent] = useState("");
637
695
  const [metadata, setMetadata] = useState("");
638
696
  const [error, setError] = useState(null);
639
- const isValid = title.trim() && content.trim() && content.length <= MAX_CONTENT_LENGTH;
697
+ const isValid = title.trim() && content.trim();
640
698
  const contentLength = content.length;
699
+ const willChunk = contentLength > CHUNK_SIZE;
700
+ const estimatedChunks = willChunk ? Math.ceil(contentLength / (CHUNK_SIZE - 200)) : 1;
641
701
  function parseMetadata() {
642
702
  if (!metadata.trim()) return null;
643
703
  try {
@@ -650,7 +710,7 @@ function CreateEmbeddings() {
650
710
  e.preventDefault();
651
711
  e.stopPropagation();
652
712
  if (!isValid) {
653
- setError("Please provide a title and content (max 4000 characters)");
713
+ setError("Please provide a title and content");
654
714
  return;
655
715
  }
656
716
  if (metadata.trim()) {
@@ -685,12 +745,18 @@ function CreateEmbeddings() {
685
745
  id: "CreateEmbeddings.header.title",
686
746
  defaultMessage: "Create Embedding"
687
747
  }),
688
- subtitle: formatMessage(
748
+ subtitle: willChunk ? formatMessage(
749
+ {
750
+ id: "CreateEmbeddings.header.subtitle.chunked",
751
+ defaultMessage: "Content: {length} characters (will create ~{chunks} embeddings)"
752
+ },
753
+ { length: contentLength, chunks: estimatedChunks }
754
+ ) : formatMessage(
689
755
  {
690
756
  id: "CreateEmbeddings.header.subtitle",
691
- defaultMessage: "Content: {length}/{max} characters"
757
+ defaultMessage: "Content: {length} characters"
692
758
  },
693
- { length: contentLength, max: MAX_CONTENT_LENGTH }
759
+ { length: contentLength }
694
760
  ),
695
761
  primaryAction: /* @__PURE__ */ jsx(
696
762
  Button,
@@ -745,6 +811,23 @@ const StyledTypography = styled(Typography)`
745
811
  display: block;
746
812
  margin-bottom: 1rem;
747
813
  `;
814
+ const ChunkTab = styled.div`
815
+ padding: 0.5rem 1rem;
816
+ cursor: pointer;
817
+ border-bottom: 2px solid ${({ $isActive }) => $isActive ? "#4945ff" : "transparent"};
818
+ color: ${({ $isActive }) => $isActive ? "#4945ff" : "inherit"};
819
+ font-weight: ${({ $isActive }) => $isActive ? "600" : "400"};
820
+ white-space: nowrap;
821
+
822
+ &:hover {
823
+ color: #4945ff;
824
+ }
825
+ `;
826
+ const ChunkTabsContainer = styled(Flex)`
827
+ border-bottom: 1px solid #dcdce4;
828
+ overflow-x: auto;
829
+ margin-bottom: 1rem;
830
+ `;
748
831
  function Metadata({ data }) {
749
832
  const metadata = {
750
833
  id: data.documentId,
@@ -801,6 +884,8 @@ function EmbeddingDetails() {
801
884
  const { del, get, put } = useFetchClient();
802
885
  const { toggleNotification } = useNotification();
803
886
  const [data, setData] = useState(null);
887
+ const [chunks, setChunks] = useState([]);
888
+ const [activeChunkIndex, setActiveChunkIndex] = useState(0);
804
889
  const [isLoading, setIsLoading] = useState(true);
805
890
  const [isDeleting, setIsDeleting] = useState(false);
806
891
  const [isEditing, setIsEditing] = useState(false);
@@ -808,6 +893,8 @@ function EmbeddingDetails() {
808
893
  const [editTitle, setEditTitle] = useState("");
809
894
  const [editContent, setEditContent] = useState("");
810
895
  const [editMetadata, setEditMetadata] = useState("");
896
+ const isChunkedDocument = chunks.length > 1;
897
+ const activeChunk = isChunkedDocument ? chunks[activeChunkIndex] : data;
811
898
  useEffect(() => {
812
899
  async function fetchData() {
813
900
  if (!id) return;
@@ -815,9 +902,31 @@ function EmbeddingDetails() {
815
902
  const response = await get(`/${PLUGIN_ID}/embeddings/find/${id}`);
816
903
  const embeddingData = response.data;
817
904
  setData(embeddingData);
818
- setEditTitle(embeddingData.title || "");
819
- setEditContent(embeddingData.content || "");
820
- setEditMetadata(embeddingData.metadata ? JSON.stringify(embeddingData.metadata, null, 2) : "");
905
+ const chunksResponse = await get(`/${PLUGIN_ID}/embeddings/related-chunks/${id}`);
906
+ const relatedChunks = chunksResponse.data?.data || [];
907
+ if (relatedChunks.length > 1) {
908
+ const sortedChunks = relatedChunks.sort((a, b) => {
909
+ const aIndex = a.metadata?.chunkIndex ?? 0;
910
+ const bIndex = b.metadata?.chunkIndex ?? 0;
911
+ return aIndex - bIndex;
912
+ });
913
+ setChunks(sortedChunks);
914
+ const currentIndex = sortedChunks.findIndex(
915
+ (chunk) => chunk.documentId === id
916
+ );
917
+ if (currentIndex >= 0) {
918
+ setActiveChunkIndex(currentIndex);
919
+ }
920
+ const firstChunk = sortedChunks[currentIndex >= 0 ? currentIndex : 0];
921
+ setEditTitle(firstChunk.title || "");
922
+ setEditContent(firstChunk.content || "");
923
+ setEditMetadata(firstChunk.metadata ? JSON.stringify(firstChunk.metadata, null, 2) : "");
924
+ } else {
925
+ setChunks([embeddingData]);
926
+ setEditTitle(embeddingData.title || "");
927
+ setEditContent(embeddingData.content || "");
928
+ setEditMetadata(embeddingData.metadata ? JSON.stringify(embeddingData.metadata, null, 2) : "");
929
+ }
821
930
  } catch (error) {
822
931
  console.error("Failed to fetch embedding:", error);
823
932
  } finally {
@@ -826,11 +935,24 @@ function EmbeddingDetails() {
826
935
  }
827
936
  fetchData();
828
937
  }, [id, get]);
938
+ useEffect(() => {
939
+ if (activeChunk && !isEditing) {
940
+ setEditTitle(activeChunk.title || "");
941
+ setEditContent(activeChunk.content || "");
942
+ setEditMetadata(activeChunk.metadata ? JSON.stringify(activeChunk.metadata, null, 2) : "");
943
+ }
944
+ }, [activeChunkIndex, activeChunk, isEditing]);
829
945
  const handleDelete = async () => {
830
- if (!id || isDeleting) return;
946
+ if (!activeChunk || isDeleting) return;
831
947
  setIsDeleting(true);
832
948
  try {
833
- await del(`/${PLUGIN_ID}/embeddings/delete-embedding/${id}`);
949
+ if (isChunkedDocument) {
950
+ for (const chunk of chunks) {
951
+ await del(`/${PLUGIN_ID}/embeddings/delete-embedding/${chunk.documentId}`);
952
+ }
953
+ } else {
954
+ await del(`/${PLUGIN_ID}/embeddings/delete-embedding/${activeChunk.documentId}`);
955
+ }
834
956
  navigate(`/plugins/${PLUGIN_ID}`);
835
957
  } catch (error) {
836
958
  console.error("Failed to delete embedding:", error);
@@ -838,18 +960,23 @@ function EmbeddingDetails() {
838
960
  }
839
961
  };
840
962
  const handleStartEdit = () => {
841
- if (data) {
842
- setEditTitle(data.title || "");
843
- setEditContent(data.content || "");
844
- setEditMetadata(data.metadata ? JSON.stringify(data.metadata, null, 2) : "");
963
+ if (activeChunk) {
964
+ setEditTitle(activeChunk.title || "");
965
+ setEditContent(activeChunk.content || "");
966
+ setEditMetadata(activeChunk.metadata ? JSON.stringify(activeChunk.metadata, null, 2) : "");
845
967
  }
846
968
  setIsEditing(true);
847
969
  };
848
970
  const handleCancelEdit = () => {
849
971
  setIsEditing(false);
972
+ if (activeChunk) {
973
+ setEditTitle(activeChunk.title || "");
974
+ setEditContent(activeChunk.content || "");
975
+ setEditMetadata(activeChunk.metadata ? JSON.stringify(activeChunk.metadata, null, 2) : "");
976
+ }
850
977
  };
851
978
  const handleSave = async () => {
852
- if (!id || isSaving) return;
979
+ if (!activeChunk || isSaving) return;
853
980
  let parsedMetadata = null;
854
981
  if (editMetadata.trim()) {
855
982
  try {
@@ -864,18 +991,26 @@ function EmbeddingDetails() {
864
991
  }
865
992
  setIsSaving(true);
866
993
  try {
867
- const response = await put(`/${PLUGIN_ID}/embeddings/update-embedding/${id}`, {
994
+ const response = await put(`/${PLUGIN_ID}/embeddings/update-embedding/${activeChunk.documentId}`, {
868
995
  data: {
869
996
  title: editTitle.trim(),
870
997
  content: editContent.trim(),
871
998
  metadata: parsedMetadata
872
999
  }
873
1000
  });
874
- setData(response.data);
1001
+ const updatedData = response.data;
1002
+ if (isChunkedDocument) {
1003
+ const newChunks = [...chunks];
1004
+ newChunks[activeChunkIndex] = updatedData;
1005
+ setChunks(newChunks);
1006
+ } else {
1007
+ setData(updatedData);
1008
+ setChunks([updatedData]);
1009
+ }
875
1010
  setIsEditing(false);
876
1011
  toggleNotification({
877
1012
  type: "success",
878
- message: "Embedding updated successfully"
1013
+ message: "Chunk updated successfully"
879
1014
  });
880
1015
  } catch (error) {
881
1016
  console.error("Failed to update embedding:", error);
@@ -887,6 +1022,20 @@ function EmbeddingDetails() {
887
1022
  setIsSaving(false);
888
1023
  }
889
1024
  };
1025
+ const handleChunkSelect = (index) => {
1026
+ if (isEditing) {
1027
+ const confirmed = globalThis.confirm("You have unsaved changes. Discard them?");
1028
+ if (!confirmed) return;
1029
+ setIsEditing(false);
1030
+ }
1031
+ setActiveChunkIndex(index);
1032
+ };
1033
+ const getOriginalTitle = () => {
1034
+ if (isChunkedDocument && chunks[0]?.metadata) {
1035
+ return chunks[0].metadata.originalTitle || chunks[0].title?.replace(/\s*\[Part \d+\/\d+\]$/, "");
1036
+ }
1037
+ return data?.title || "Embedding Details";
1038
+ };
890
1039
  if (isLoading) {
891
1040
  return /* @__PURE__ */ jsxs(Main, { children: [
892
1041
  /* @__PURE__ */ jsx(
@@ -899,7 +1048,7 @@ function EmbeddingDetails() {
899
1048
  /* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsx(Loader, { children: "Loading embedding details..." }) }) })
900
1049
  ] });
901
1050
  }
902
- if (!data) {
1051
+ if (!data || !activeChunk) {
903
1052
  return /* @__PURE__ */ jsxs(Main, { children: [
904
1053
  /* @__PURE__ */ jsx(
905
1054
  Layouts.Header,
@@ -911,68 +1060,103 @@ function EmbeddingDetails() {
911
1060
  /* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsx(Box, { padding: 8, textAlign: "center", children: /* @__PURE__ */ jsx(Typography, { children: "The requested embedding could not be found." }) }) })
912
1061
  ] });
913
1062
  }
1063
+ const headerTitle = isEditing ? `Edit: ${activeChunk.title}` : isChunkedDocument ? getOriginalTitle() : data.title || "Embedding Details";
1064
+ const headerSubtitle = isChunkedDocument ? `Chunked Document (${chunks.length} parts)` : `Embedding ID: ${data.embeddingId || "N/A"}`;
914
1065
  return /* @__PURE__ */ jsxs(Main, { children: [
915
1066
  /* @__PURE__ */ jsx(
916
1067
  Layouts.Header,
917
1068
  {
918
- title: isEditing ? "Edit Embedding" : data.title || "Embedding Details",
919
- subtitle: `Embedding ID: ${data.embeddingId || "N/A"}`,
1069
+ title: headerTitle,
1070
+ subtitle: headerSubtitle,
920
1071
  primaryAction: isEditing ? /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
921
1072
  /* @__PURE__ */ jsx(Button, { variant: "tertiary", startIcon: /* @__PURE__ */ jsx(Cross, {}), onClick: handleCancelEdit, children: "Cancel" }),
922
- /* @__PURE__ */ jsx(Button, { startIcon: /* @__PURE__ */ jsx(Check, {}), onClick: handleSave, loading: isSaving, children: isSaving ? "Saving..." : "Save" })
1073
+ /* @__PURE__ */ jsx(Button, { startIcon: /* @__PURE__ */ jsx(Check, {}), onClick: handleSave, loading: isSaving, children: isSaving ? "Saving..." : "Save Chunk" })
923
1074
  ] }) : /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
924
- /* @__PURE__ */ jsx(Button, { variant: "secondary", startIcon: /* @__PURE__ */ jsx(Pencil, {}), onClick: handleStartEdit, children: "Edit" }),
1075
+ /* @__PURE__ */ jsxs(Button, { variant: "secondary", startIcon: /* @__PURE__ */ jsx(Pencil, {}), onClick: handleStartEdit, children: [
1076
+ "Edit ",
1077
+ isChunkedDocument ? "Chunk" : ""
1078
+ ] }),
925
1079
  /* @__PURE__ */ jsx(ConfirmDeleteEmbedding, { onConfirm: handleDelete, isLoading: isDeleting })
926
1080
  ] }),
927
1081
  navigationAction: /* @__PURE__ */ jsx(BackLink, { to: `/plugins/${PLUGIN_ID}` })
928
1082
  }
929
1083
  ),
930
- /* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsx(Box, { padding: 8, children: isEditing ? /* @__PURE__ */ jsxs(Grid.Root, { gap: 6, children: [
931
- /* @__PURE__ */ jsx(Grid.Item, { col: 8, s: 12, children: /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 1, hasRadius: true, children: /* @__PURE__ */ jsxs(Box, { padding: 4, background: "neutral0", hasRadius: true, children: [
932
- /* @__PURE__ */ jsx(Box, { marginBottom: 4, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
933
- /* @__PURE__ */ jsx(Field.Label, { children: "Title" }),
1084
+ /* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsxs(Box, { padding: 8, children: [
1085
+ isChunkedDocument && /* @__PURE__ */ jsx(Box, { marginBottom: 4, children: /* @__PURE__ */ jsxs(Box, { background: "neutral0", padding: 4, hasRadius: true, children: [
1086
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, children: [
1087
+ /* @__PURE__ */ jsx(Typography, { variant: "beta", children: "Document Chunks" }),
1088
+ /* @__PURE__ */ jsxs(Badge, { children: [
1089
+ chunks.length,
1090
+ " parts"
1091
+ ] })
1092
+ ] }),
1093
+ /* @__PURE__ */ jsx(ChunkTabsContainer, { gap: 0, children: chunks.map((chunk, index) => /* @__PURE__ */ jsxs(
1094
+ ChunkTab,
1095
+ {
1096
+ $isActive: index === activeChunkIndex,
1097
+ onClick: () => handleChunkSelect(index),
1098
+ children: [
1099
+ "Part ",
1100
+ index + 1,
1101
+ index === activeChunkIndex && isEditing && " (editing)"
1102
+ ]
1103
+ },
1104
+ chunk.documentId
1105
+ )) })
1106
+ ] }) }),
1107
+ isEditing ? /* @__PURE__ */ jsxs(Grid.Root, { gap: 6, children: [
1108
+ /* @__PURE__ */ jsx(Grid.Item, { col: 8, s: 12, children: /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 1, hasRadius: true, children: /* @__PURE__ */ jsxs(Box, { padding: 4, background: "neutral0", hasRadius: true, children: [
1109
+ /* @__PURE__ */ jsx(Box, { marginBottom: 4, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
1110
+ /* @__PURE__ */ jsx(Field.Label, { children: "Title" }),
1111
+ /* @__PURE__ */ jsx(
1112
+ TextInput,
1113
+ {
1114
+ value: editTitle,
1115
+ onChange: (e) => setEditTitle(e.target.value)
1116
+ }
1117
+ )
1118
+ ] }) }),
1119
+ /* @__PURE__ */ jsxs(Box, { marginBottom: 4, children: [
1120
+ /* @__PURE__ */ jsxs(Field.Root, { children: [
1121
+ /* @__PURE__ */ jsx(Field.Label, { children: "Content" }),
1122
+ /* @__PURE__ */ jsx(Field.Hint, { children: "Changes to content will regenerate the embedding vector" })
1123
+ ] }),
1124
+ /* @__PURE__ */ jsx(
1125
+ MarkdownEditor,
1126
+ {
1127
+ content: editContent,
1128
+ onChange: setEditContent,
1129
+ height: 300
1130
+ }
1131
+ )
1132
+ ] })
1133
+ ] }) }) }),
1134
+ /* @__PURE__ */ jsx(Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 1, hasRadius: true, children: /* @__PURE__ */ jsx(Box, { padding: 4, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsx(Box, { marginBottom: 4, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
1135
+ /* @__PURE__ */ jsx(Field.Label, { children: "Metadata (JSON)" }),
1136
+ /* @__PURE__ */ jsx(Field.Hint, { children: "Optional custom metadata" }),
934
1137
  /* @__PURE__ */ jsx(
935
- TextInput,
1138
+ Textarea,
936
1139
  {
937
- value: editTitle,
938
- onChange: (e) => setEditTitle(e.target.value)
1140
+ value: editMetadata,
1141
+ onChange: (e) => setEditMetadata(e.target.value),
1142
+ placeholder: '{"key": "value"}'
939
1143
  }
940
1144
  )
941
- ] }) }),
942
- /* @__PURE__ */ jsxs(Box, { marginBottom: 4, children: [
943
- /* @__PURE__ */ jsxs(Field.Root, { children: [
944
- /* @__PURE__ */ jsx(Field.Label, { children: "Content" }),
945
- /* @__PURE__ */ jsx(Field.Hint, { children: "Changes to content will regenerate the embedding vector" })
1145
+ ] }) }) }) }) })
1146
+ ] }) : /* @__PURE__ */ jsxs(Grid.Root, { gap: 6, children: [
1147
+ /* @__PURE__ */ jsx(Grid.Item, { col: 8, s: 12, children: /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 1, hasRadius: true, children: /* @__PURE__ */ jsxs(Box, { padding: 4, background: "neutral0", hasRadius: true, children: [
1148
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, children: [
1149
+ /* @__PURE__ */ jsx(StyledTypography, { variant: "beta", style: { margin: 0 }, children: isChunkedDocument ? `Part ${activeChunkIndex + 1} Content` : "Embedding Content" }),
1150
+ isChunkedDocument && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
1151
+ activeChunk.content?.length || 0,
1152
+ " characters"
1153
+ ] })
946
1154
  ] }),
947
- /* @__PURE__ */ jsx(
948
- MarkdownEditor,
949
- {
950
- content: editContent,
951
- onChange: setEditContent,
952
- height: 300
953
- }
954
- )
955
- ] })
956
- ] }) }) }),
957
- /* @__PURE__ */ jsx(Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 1, hasRadius: true, children: /* @__PURE__ */ jsx(Box, { padding: 4, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsx(Box, { marginBottom: 4, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
958
- /* @__PURE__ */ jsx(Field.Label, { children: "Metadata (JSON)" }),
959
- /* @__PURE__ */ jsx(Field.Hint, { children: "Optional custom metadata" }),
960
- /* @__PURE__ */ jsx(
961
- Textarea,
962
- {
963
- value: editMetadata,
964
- onChange: (e) => setEditMetadata(e.target.value),
965
- placeholder: '{"key": "value"}'
966
- }
967
- )
968
- ] }) }) }) }) })
969
- ] }) : /* @__PURE__ */ jsxs(Grid.Root, { gap: 6, children: [
970
- /* @__PURE__ */ jsx(Grid.Item, { col: 8, s: 12, children: /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 1, hasRadius: true, children: /* @__PURE__ */ jsxs(Box, { padding: 4, background: "neutral0", hasRadius: true, children: [
971
- /* @__PURE__ */ jsx(StyledTypography, { variant: "beta", children: "Embedding Content" }),
972
- data.content ? /* @__PURE__ */ jsx(Markdown, { children: data.content }) : /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: "No content" })
973
- ] }) }) }),
974
- /* @__PURE__ */ jsx(Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 1, hasRadius: true, children: /* @__PURE__ */ jsx(Metadata, { data }) }) })
975
- ] }) }) })
1155
+ activeChunk.content ? /* @__PURE__ */ jsx(Markdown, { children: activeChunk.content }) : /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: "No content" })
1156
+ ] }) }) }),
1157
+ /* @__PURE__ */ jsx(Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 1, hasRadius: true, children: /* @__PURE__ */ jsx(Metadata, { data: activeChunk }) }) })
1158
+ ] })
1159
+ ] }) })
976
1160
  ] });
977
1161
  }
978
1162
  const App = () => {