UNPKG

@llamaindex/ui

Version:

A comprehensive UI component library built with React, TypeScript, and Tailwind CSS for LlamaIndex applications

1,387 lines (1,378 loc) 65.3 kB
import { Popover, PopoverTrigger, PopoverContent } from './chunk-AT2HHFS3.mjs'; import { Label } from './chunk-HTDT6UBX.mjs'; import { Slider } from './chunk-IPGFBZIN.mjs'; import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from './chunk-WVWSZ6KQ.mjs'; import { Tooltip, TooltipTrigger, TooltipContent } from './chunk-FSJOM7AM.mjs'; import { Textarea } from './chunk-KDZ6LO7J.mjs'; import { isNullable } from './chunk-YHVLD5CA.mjs'; import { useUIConfigStore } from './chunk-QQG6BRXU.mjs'; import { Table, TableHeader, TableBody, TableRow, TableCell, TableHead } from './chunk-Y27WI3E4.mjs'; import { Input } from './chunk-EWMUBFVD.mjs'; import { Button } from './chunk-NLLOGSY3.mjs'; import { cn } from './chunk-MG2ARK3A.mjs'; import { __spreadProps, __spreadValues } from './chunk-FWCSY2DS.mjs'; import React2, { useState, useRef, useLayoutEffect, useMemo, useEffect, useCallback } from 'react'; import { jsxs, jsx, Fragment } from 'react/jsx-runtime'; import { Trash2, Plus } from 'lucide-react'; import { toast } from 'sonner'; // src/extracted-data/confidence-utils.ts function getConfidenceBackgroundClass(threshold, confidence) { if (!confidence || confidence === 0) return ""; return confidence < threshold ? "bg-orange-50" : ""; } function getConfidenceBorderClass(threshold, confidence) { if (!confidence || confidence === 0) return ""; return confidence < threshold ? "border-b-2 border-orange-300" : "border-b border-gray-200"; } // src/extracted-data/primitive-validation.ts function toPrimitiveType(schemaType) { switch (schemaType) { case "number": return "number" /* NUMBER */; case "boolean": return "boolean" /* BOOLEAN */; case "string": default: return "string" /* STRING */; } } function convertPrimitiveValue(inputValue, expectedType, required = false) { switch (expectedType) { case "string" /* STRING */: return inputValue; case "number" /* NUMBER */: if (inputValue === "") { return required ? "" : null; } return Number(inputValue); case "boolean" /* BOOLEAN */: return inputValue === "true"; default: return inputValue; } } function EditableField({ value, onSave, isChanged, showBorder = true, expectedType = "string" /* STRING */, required = false, className, metadata, onClick, editable = true }) { var _a; const confidenceThreshold = useUIConfigStore( (state) => state.confidenceThreshold ); const [isOpen, setIsOpen] = useState(false); const [editValue, setEditValue] = useState( value === null || value === void 0 ? "" : String(value) ); const [localConfidence, setLocalConfidence] = useState( (_a = metadata == null ? void 0 : metadata.confidence) != null ? _a : 1 ); const containerRef = useRef(null); useLayoutEffect(() => { if (containerRef.current) { const parent = containerRef.current.parentElement; if (parent) { const contentHeight = parent.clientHeight; containerRef.current.style.height = `${contentHeight}px`; } } }, [value]); const handleSave = () => { if (required && expectedType === "number" /* NUMBER */ && editValue.trim() === "") { return; } const convertedValue = convertPrimitiveValue( editValue, expectedType, required ); onSave(convertedValue); setLocalConfidence(1); setIsOpen(false); }; const handleCancel = () => { setEditValue(value === null || value === void 0 ? "" : String(value)); setIsOpen(false); }; const handleOpenChange = (open) => { setIsOpen(open); if (open) { setEditValue(value === null || value === void 0 ? "" : String(value)); } }; const handleKeyDown = (e) => { if (e.key === "Enter" && !e.shiftKey && !e.ctrlKey && !e.metaKey) { e.preventDefault(); handleSave(); } if (e.key === "Escape") { handleCancel(); } }; const handleInputChange = (e) => { setEditValue(e.target.value); }; const handleTextareaChange = (e) => { setEditValue(e.target.value); }; const handleSelectChange = (value2) => { setEditValue(value2); }; const currentConfidence = localConfidence != null ? localConfidence : metadata == null ? void 0 : metadata.confidence; const displayValue = value === null || value === void 0 || value === "" ? "" : String(value); const backgroundClass = isChanged ? "bg-green-50" : getConfidenceBackgroundClass(confidenceThreshold, currentConfidence); const hoverClass = backgroundClass.includes("bg-orange-50") ? "hover:bg-orange-100" : backgroundClass.includes("bg-green-50") ? "hover:bg-green-100" : "hover:bg-gray-100"; const defaultBorderClass = showBorder ? getConfidenceBorderClass(confidenceThreshold, currentConfidence) : ""; const paddingClass = "p-2"; const renderEditInput = () => { switch (expectedType) { case "boolean" /* BOOLEAN */: return /* @__PURE__ */ jsxs(Select, { onValueChange: handleSelectChange, value: editValue, children: [ /* @__PURE__ */ jsx(SelectTrigger, { className: "w-full", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select a value" }) }), /* @__PURE__ */ jsxs(SelectContent, { children: [ /* @__PURE__ */ jsx(SelectItem, { value: "true", children: "true" }), /* @__PURE__ */ jsx(SelectItem, { value: "false", children: "false" }) ] }) ] }); case "string" /* STRING */: return /* @__PURE__ */ jsx( Textarea, { value: editValue, onChange: handleTextareaChange, onKeyDown: handleKeyDown, placeholder: "Enter value", className: "w-full min-h-[100px] resize-none", autoFocus: true } ); case "number" /* NUMBER */: return /* @__PURE__ */ jsx( Input, { type: "number", value: editValue, onChange: handleInputChange, onKeyDown: handleKeyDown, placeholder: "Enter a number", className: "w-full", autoFocus: true, required } ); default: return /* @__PURE__ */ jsx( Input, { value: editValue, onChange: handleInputChange, onKeyDown: handleKeyDown, placeholder: "Enter value", className: "w-full", autoFocus: true } ); } }; if (!editable) { return /* @__PURE__ */ jsxs(Tooltip, { children: [ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx( "div", { ref: containerRef, onClick: () => onClick == null ? void 0 : onClick({ value, metadata }), className: `cursor-pointer ${showBorder ? "min-h-8" : "w-full"} flex items-center ${defaultBorderClass} ${paddingClass} ${backgroundClass} ${hoverClass} ${className}`, "data-testid": "editable-field-trigger", children: /* @__PURE__ */ jsx("span", { className: "text-sm accent-foreground truncate leading-tight block w-full", children: displayValue }) } ) }), /* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: displayValue }) }) ] }); } return /* @__PURE__ */ jsxs(Popover, { open: isOpen, onOpenChange: handleOpenChange, children: [ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx( "div", { ref: containerRef, onClick: () => onClick == null ? void 0 : onClick({ value, metadata }), className: `cursor-pointer ${showBorder ? "min-h-8" : "w-full"} flex items-center ${defaultBorderClass} ${paddingClass} ${backgroundClass} ${hoverClass} ${className}`, "data-testid": "editable-field-trigger", children: /* @__PURE__ */ jsx("span", { className: "text-sm accent-foreground truncate leading-tight block w-full", children: displayValue }) } ) }), /* @__PURE__ */ jsx(PopoverContent, { className: "w-80", align: "start", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", "data-testid": "editable-field-popover", children: [ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-700", children: "Edit Value" }) }), renderEditInput(), /* @__PURE__ */ jsxs("div", { className: "flex justify-end space-x-2", children: [ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: handleCancel, children: "Cancel" }), /* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleSave, children: "Save" }) ] }) ] }) }) ] }); } // src/extracted-data/schema-metadata-lookup.ts function normalizeMetadataPath(pathString) { return pathString.replace(/\.\d+(?=\.|$)/g, ".*"); } function lookupFieldSchemaMetadata(keyPath, fieldSchemaMetadata) { const pathString = keyPath.join("."); let metadata = fieldSchemaMetadata[pathString]; if (metadata) { return metadata; } const normalizedPath = normalizeMetadataPath(pathString); metadata = fieldSchemaMetadata[normalizedPath]; if (metadata) { return metadata; } if (!pathString.includes(".") || !pathString.match(/\.\d+\./)) { const segments = keyPath; if (segments.length > 1) { for (let i = 1; i < segments.length; i++) { const wildcardPath = [ ...segments.slice(0, i), "*", ...segments.slice(i) ].join("."); metadata = fieldSchemaMetadata[wildcardPath]; if (metadata) { return metadata; } } } } return metadata; } function getDefaultValueForType(schemaType) { const PrimitiveType2 = { STRING: "string", NUMBER: "number", BOOLEAN: "boolean" }; switch (schemaType) { case PrimitiveType2.STRING: return ""; case PrimitiveType2.NUMBER: return 0; case PrimitiveType2.BOOLEAN: return false; default: return ""; } } // src/extracted-data/table-renderer/table-renderer-utils.ts var formatFieldName = (key) => { return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (l) => l.toUpperCase()).trim(); }; function isTableCellChanged(changedPaths, keyPath, rowIndex, columnKey) { if (!changedPaths) return false; const relativeCellPath = `${rowIndex}.${columnKey}`; const absoluteCellPath = [...keyPath, String(rowIndex), columnKey].join("."); return changedPaths.has(relativeCellPath) || changedPaths.has(absoluteCellPath); } function flattenObject(obj, prefix = []) { const columns = []; Object.keys(obj).forEach((key) => { const value = obj[key]; const currentPath = [...prefix, key]; if (value !== null && value !== void 0 && typeof value === "object" && !Array.isArray(value)) { const nestedColumns = flattenObject( value, currentPath ); columns.push(...nestedColumns); } else if (Array.isArray(value)) { columns.push({ key: currentPath.join("."), header: formatFieldName(key), path: currentPath, isLeaf: true }); } else if (value !== null && value !== void 0) { columns.push({ key: currentPath.join("."), header: formatFieldName(key), path: currentPath, isLeaf: true }); } }); return columns; } var getValue = (item, column) => { let current = item; for (const key of column.path) { if (current && typeof current === "object" && !Array.isArray(current)) { current = current[key]; } else { return void 0; } } return current; }; var handleUpdate = (rowIndex, column, value, data, onUpdate) => { if (column.path.length === 1) { const affectedPath = `${rowIndex}.${column.path[0]}`; onUpdate(rowIndex, column.path[0], value, [affectedPath]); } else { const rootKey = column.path[0]; const currentRootObj = data[rowIndex][rootKey] || {}; const updatedObj = JSON.parse(JSON.stringify(currentRootObj)); let current = updatedObj; for (let i = 1; i < column.path.length - 1; i++) { const key = column.path[i]; if (!current[key] || typeof current[key] !== "object") { current[key] = {}; } current = current[key]; } const finalKey = column.path[column.path.length - 1]; current[finalKey] = value; const affectedPath = `${rowIndex}.${column.key}`; onUpdate(rowIndex, rootKey, updatedObj, [affectedPath]); } }; function getTableRowDefaultValue(keyPath, fieldMetadata) { const newRow = {}; const arrayPath = keyPath.join("."); Object.entries(fieldMetadata).forEach(([path, metadata]) => { const pathParts = path.split("."); if (pathParts.length > keyPath.length + 1) { const pathArrayPart = pathParts.slice(0, keyPath.length).join("."); if (pathArrayPart === arrayPath) { const fieldPath = pathParts.slice(keyPath.length + 1); const defaultValue = getFieldDefaultValue(metadata); setNestedValue(newRow, fieldPath, defaultValue); } } }); return newRow; } function getFieldDefaultValue(metadata) { if (!metadata || !metadata.schemaType) { return ""; } switch (metadata.schemaType) { case "array": return []; // Empty array for array fields case "object": return {}; // Empty object for nested objects default: return getDefaultValueForType(metadata.schemaType); } } function setNestedValue(obj, path, value) { if (path.length === 1) { obj[path[0]] = value; } else { if (!obj[path[0]] || typeof obj[path[0]] !== "object") { obj[path[0]] = {}; } setNestedValue( obj[path[0]], path.slice(1), value ); } } // src/extracted-data/metadata-path-utils.ts function buildTableHeaderMetadataPath(parentKeyPath, columnPath, depth) { return [...parentKeyPath, "0", ...columnPath.slice(0, depth + 1)]; } function findFieldSchemaMetadata(keyPath, fieldSchemaMetadata) { return lookupFieldSchemaMetadata(keyPath, fieldSchemaMetadata); } function formatFieldName2(key) { return key.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()); } function getFieldDisplayInfo(key, fieldSchemaMetadata, validationErrors = [], keyPath = [key]) { var _a, _b; const pathString = keyPath.join("."); const metadata = findFieldSchemaMetadata(keyPath, fieldSchemaMetadata); const displayName = (metadata == null ? void 0 : metadata.title) || formatFieldName2(key); const fieldError = validationErrors.find( (error) => error.path.join(".") === pathString ); return { name: displayName, isRequired: (_a = metadata == null ? void 0 : metadata.isRequired) != null ? _a : false, wasMissing: (_b = metadata == null ? void 0 : metadata.wasMissing) != null ? _b : false, hasError: !!fieldError, errorMessage: fieldError == null ? void 0 : fieldError.message }; } function getFieldLabelClasses(fieldInfo) { const baseClasses = "text-sm font-medium text-zinc-900 min-w-0 flex-shrink-0 min-h-8 flex items-center"; if (fieldInfo.hasError) { return `${baseClasses} text-red-600`; } if (fieldInfo.isRequired) { return `${baseClasses} font-semibold`; } return baseClasses; } function getFieldLabelText(fieldInfo) { const { name, isRequired } = fieldInfo; if (isRequired) { return /* @__PURE__ */ jsxs(Fragment, { children: [ name, " ", /* @__PURE__ */ jsx("span", { className: "text-red-500 relative top-0.5", children: "\xA0*" }) ] }); } return name; } // src/extracted-data/metadata-lookup.ts function findExtractedFieldMetadata(path, metadata) { const pathArray = Array.isArray(path) ? path : path.split("."); let current = metadata; for (let i = 0; i < pathArray.length; i++) { const segment = pathArray[i]; if (!current || typeof current !== "object") { return void 0; } if (!isNaN(Number(segment))) { if (Array.isArray(current)) { const index = Number(segment); current = current[index]; } else { return void 0; } } else { current = current[segment]; } if (isExtractedFieldMetadata(current)) { return current; } } return void 0; } function isExtractedFieldMetadata(value) { if (value === null || typeof value !== "object") { return false; } const obj = value; if ("confidence" in obj && typeof obj.confidence === "number") { return true; } return false; } function TableRenderer({ data, onUpdate, onAddRow, onDeleteRow, changedPaths, keyPath = [], metadata, validationErrors = [], onClickField, editable = true }) { var _a, _b; const effectiveMetadata = { schema: (_a = metadata == null ? void 0 : metadata.schema) != null ? _a : {}, extracted: (_b = metadata == null ? void 0 : metadata.extracted) != null ? _b : {} }; const getMetadata = (path) => { if (effectiveMetadata.extracted) { return findExtractedFieldMetadata(path, effectiveMetadata.extracted); } return void 0; }; const handleAddRow = () => { const schemaBasedRow = getTableRowDefaultValue( keyPath, effectiveMetadata.schema ); if (Object.keys(schemaBasedRow).length > 0) { onAddRow == null ? void 0 : onAddRow(schemaBasedRow); return; } const newRow = {}; if (data.length > 0) { const firstRow = data[0]; const fillEmptyValues = (obj) => { const result = {}; Object.keys(obj).forEach((key) => { const value = obj[key]; if (value !== null && typeof value === "object" && !Array.isArray(value)) { result[key] = fillEmptyValues(value); } else { result[key] = ""; } }); return result; }; Object.assign(newRow, fillEmptyValues(firstRow)); } else { columns.forEach((column) => { const setNestedValue2 = (obj, path, value) => { if (path.length === 1) { obj[path[0]] = value; } else { if (!obj[path[0]] || typeof obj[path[0]] !== "object" || Array.isArray(obj[path[0]])) { obj[path[0]] = {}; } setNestedValue2(obj[path[0]], path.slice(1), value); } }; setNestedValue2(newRow, column.path, ""); }); } onAddRow == null ? void 0 : onAddRow(newRow); }; const handleDeleteRow = (index) => { onDeleteRow == null ? void 0 : onDeleteRow(index); }; const { columns, maxDepth } = useMemo(() => { const allColumns = /* @__PURE__ */ new Map(); const columnOrder = []; if (data && data.length > 0) { data.forEach((item) => { const itemColumns = flattenObject(item); itemColumns.forEach((col) => { if (!allColumns.has(col.key)) { allColumns.set(col.key, col); columnOrder.push(col.key); } }); }); } else if (effectiveMetadata.schema && Object.keys(effectiveMetadata.schema).length > 0) { Object.keys(effectiveMetadata.schema).forEach((path) => { const pathParts = path.split("."); if (pathParts.length > keyPath.length + 1) { const fieldPath = pathParts.slice(keyPath.length + 1); const fieldKey = fieldPath.join("."); if (!allColumns.has(fieldKey)) { const columnDef = { key: fieldKey, header: fieldPath[fieldPath.length - 1], // Use the last part as header path: fieldPath, isLeaf: true // Schema-generated columns are always leaf nodes }; allColumns.set(fieldKey, columnDef); columnOrder.push(fieldKey); } } }); } if (columnOrder.length === 0) { return { columns: [], maxDepth: 0 }; } const columns2 = columnOrder.map((key) => allColumns.get(key)); const maxDepth2 = Math.max(...columns2.map((col) => col.path.length)); return { columns: columns2, maxDepth: maxDepth2 }; }, [data, effectiveMetadata.schema, keyPath]); const generateHeaderRows = () => { const rows = []; if (maxDepth === 0 || columns.length === 0) { return rows; } const occupiedCells = Array(maxDepth).fill(null).map(() => Array(columns.length).fill(false)); for (let depth = 0; depth < maxDepth; depth++) { const rowCells = []; let colIndex = 0; while (colIndex < columns.length) { if (occupiedCells[depth][colIndex]) { colIndex++; continue; } const column = columns[colIndex]; if (depth < column.path.length) { const fieldKey = column.path[depth]; const fieldKeyPath = buildTableHeaderMetadataPath( keyPath, column.path, depth ); const fieldInfo = getFieldDisplayInfo( fieldKey, effectiveMetadata.schema, validationErrors, fieldKeyPath ); const headerText = getFieldLabelText(fieldInfo); const isLeaf = depth === column.path.length - 1; let colSpan = 1; let nextColIndex = colIndex + 1; while (nextColIndex < columns.length) { const nextColumn = columns[nextColIndex]; if (depth < nextColumn.path.length && nextColumn.path[depth] === column.path[depth]) { if (depth === 0 || column.path.slice(0, depth).join(".") === nextColumn.path.slice(0, depth).join(".")) { colSpan++; nextColIndex++; } else { break; } } else { break; } } const rowSpan = isLeaf ? maxDepth - depth : 1; if (rowSpan > 1) { for (let r = depth + 1; r < depth + rowSpan; r++) { for (let c = colIndex; c < colIndex + colSpan; c++) { occupiedCells[r][c] = true; } } } rowCells.push( /* @__PURE__ */ jsx( TableHead, { colSpan, rowSpan, className: "px-2 py-2 text-center border-r border-gray-200 border-b min-w-[80px] max-w-[200px] bg-gray-50", style: rowSpan > 1 ? { height: `${rowSpan * 32}px` } : void 0, children: /* @__PURE__ */ jsx("div", { className: "break-words text-zinc-900 font-semibold", children: headerText }) }, `${depth}-${colIndex}` ) ); colIndex = nextColIndex; } else { colIndex++; } } rows.push(/* @__PURE__ */ jsx(TableRow, { children: rowCells }, depth)); } if (onDeleteRow && rows.length > 0) { const firstRow = rows[0]; const modifiedFirstRow = React2.cloneElement(firstRow, { children: [ ...React2.Children.toArray(firstRow.props.children), /* @__PURE__ */ jsx( TableHead, { rowSpan: maxDepth, className: "px-2 py-2 text-center border-r border-gray-200 border-b w-12 bg-gray-50", style: { height: `${maxDepth * 32}px` } }, "delete-header" ) ] }); rows[0] = modifiedFirstRow; } return rows; }; return /* @__PURE__ */ jsx("div", { className: "border border-b-0 rounded-md bg-white", children: /* @__PURE__ */ jsxs(Table, { className: "table-auto", children: [ /* @__PURE__ */ jsx(TableHeader, { children: generateHeaderRows() }), /* @__PURE__ */ jsxs(TableBody, { children: [ data.map((item, rowIndex) => /* @__PURE__ */ jsxs(TableRow, { className: "hover:bg-gray-50 border-0", children: [ columns.map((column, colIndex) => { const value = getValue(item, column); const cellPath = [...keyPath, String(rowIndex), ...column.path]; const isChanged = isTableCellChanged( changedPaths, keyPath, rowIndex, column.key ); if (Array.isArray(value)) { return /* @__PURE__ */ jsx( TableCell, { className: "p-0 border-r border-gray-100 min-w-[160px] max-w-[360px] align-top", children: /* @__PURE__ */ jsx("div", { className: "px-2 py-2 text-xs text-amber-700 bg-amber-50 border border-amber-200 rounded-sm m-1", children: "Nested list/table is not supported." }) }, colIndex ); } const fieldKeyPath = [...keyPath, "*", ...column.path]; const fieldInfo = findFieldSchemaMetadata( fieldKeyPath, effectiveMetadata.schema ); const expectedType = (fieldInfo == null ? void 0 : fieldInfo.schemaType) ? toPrimitiveType(fieldInfo.schemaType) : "string" /* STRING */; const isRequired = (fieldInfo == null ? void 0 : fieldInfo.isRequired) || false; return /* @__PURE__ */ jsx( TableCell, { className: "p-0 border-r border-gray-100 min-w-[80px] max-w-[200px]", children: /* @__PURE__ */ jsx( EditableField, { value, onSave: (newValue) => handleUpdate( rowIndex, column, newValue, data, (idx, key, val, paths) => onUpdate(idx, key, val, paths) ), metadata: getMetadata(cellPath), isChanged, showBorder: true, expectedType, required: isRequired, onClick: (args) => onClickField == null ? void 0 : onClickField({ value: args.value, metadata: args.metadata, path: cellPath }), editable } ) }, colIndex ); }), onDeleteRow && /* @__PURE__ */ jsx(TableCell, { className: "p-0 border-r border-gray-100 border-b w-12", children: /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center", children: /* @__PURE__ */ jsx( Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteRow(rowIndex), className: "h-8 w-8 text-red-500 hover:text-red-700 hover:bg-transparent cursor-pointer", title: "Delete row", children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" }) } ) }) }) ] }, rowIndex)), onAddRow && /* @__PURE__ */ jsxs(TableRow, { className: "hover:bg-gray-50", children: [ Array.from({ length: columns.length }).map((_, colIndex) => /* @__PURE__ */ jsx( TableCell, { className: "p-0 border-b min-w-[80px] max-w-[200px]" }, colIndex )), /* @__PURE__ */ jsx( TableCell, { colSpan: columns.length + (onDeleteRow ? 1 : 0), className: "border-b text-center p-0", children: /* @__PURE__ */ jsx( Button, { variant: "ghost", size: "sm", onClick: handleAddRow, className: "text-blue-600 border-blue-200 hover:bg-transparent cursor-pointer", children: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }) } ) } ) ] }) ] }) ] }) }); } // src/extracted-data/list-renderer/list-renderer-utils.ts function buildArrayItemPath(keyPath, index) { return [...keyPath, String(index)].join("."); } function isArrayItemChanged(changedPaths, keyPath, index) { if (!changedPaths) return false; const fullPath = buildArrayItemPath(keyPath, index); return changedPaths.has(fullPath); } function getArrayItemDefaultValue(keyPath, fieldMetadata) { const itemFieldPath = [...keyPath, "*"]; const itemMetadata = lookupFieldSchemaMetadata(itemFieldPath, fieldMetadata); if (!(itemMetadata == null ? void 0 : itemMetadata.schemaType)) { return ""; } return getDefaultValueForType(itemMetadata.schemaType); } function ListRenderer({ data, onUpdate, onAdd, onDelete, changedPaths, keyPath = [], metadata, onClickField, editable = true }) { var _a, _b; const effectiveSchema = (_a = metadata == null ? void 0 : metadata.schema) != null ? _a : {}; const effectiveExtracted = (_b = metadata == null ? void 0 : metadata.extracted) != null ? _b : {}; const handleAdd = () => { const defaultValue = getArrayItemDefaultValue(keyPath, effectiveSchema); onAdd == null ? void 0 : onAdd(defaultValue); }; const handleDelete = (index) => { onDelete == null ? void 0 : onDelete(index); }; const getExpectedType = () => { const itemFieldPath = [...keyPath, "*"]; const itemMetadata = findFieldSchemaMetadata( itemFieldPath, effectiveSchema ); if (itemMetadata == null ? void 0 : itemMetadata.schemaType) { return toPrimitiveType(itemMetadata.schemaType); } return "string" /* STRING */; }; const expectedType = getExpectedType(); const handleFieldClick = (args, index) => { onClickField == null ? void 0 : onClickField({ value: args.value, metadata: args.metadata, path: [...keyPath, String(index)] }); }; if (!data || data.length === 0) { return /* @__PURE__ */ jsxs("div", { className: "border rounded-md bg-white p-4", children: [ /* @__PURE__ */ jsx("div", { className: "text-gray-500 text-sm mb-3", children: "Empty array" }), onAdd && /* @__PURE__ */ jsx( Button, { variant: "outline", size: "sm", onClick: handleAdd, className: "text-blue-600 border-blue-200 hover:bg-blue-50 cursor-pointer", children: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }) } ) ] }); } return /* @__PURE__ */ jsx("div", { className: "border rounded-md bg-white overflow-auto", children: /* @__PURE__ */ jsx(Table, { className: "table-auto", children: /* @__PURE__ */ jsxs(TableBody, { children: [ data.map((item, index) => { const isChanged = isArrayItemChanged(changedPaths, keyPath, index); const handleItemFieldClick = (args) => { handleFieldClick(args, index); }; return /* @__PURE__ */ jsxs(TableRow, { className: "hover:bg-gray-50 border-0", children: [ /* @__PURE__ */ jsx(TableCell, { className: "p-0 border-r border-gray-100 w-12 align-middle h-full", children: /* @__PURE__ */ jsx("div", { className: "w-full h-full border-b flex items-center justify-center text-sm text-gray-600 font-medium bg-gray-25 p-2", children: index + 1 }) }), /* @__PURE__ */ jsx(TableCell, { className: "p-0 min-w-[120px] align-top h-full", children: /* @__PURE__ */ jsx( EditableField, { value: item, onSave: (newValue) => onUpdate(index, newValue), metadata: findExtractedFieldMetadata( [...keyPath, String(index)], effectiveExtracted ), isChanged, showBorder: true, expectedType, required: expectedType === "number" /* NUMBER */ || expectedType === "boolean" /* BOOLEAN */, onClick: handleItemFieldClick, editable } ) }), onDelete && /* @__PURE__ */ jsx(TableCell, { className: "p-0 w-12 align-middle h-full", children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center border-b", children: /* @__PURE__ */ jsx( Button, { variant: "ghost", size: "icon", onClick: () => handleDelete(index), className: "h-9 w-9 text-red-500 hover:text-red-700 hover:bg-transparent cursor-pointer rounded-none", title: "Delete item", children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" }) } ) }) }) ] }, index); }), onAdd && /* @__PURE__ */ jsxs(TableRow, { className: "hover:bg-gray-50 border-0", children: [ /* @__PURE__ */ jsx(TableCell, { className: "p-0 border-r border-gray-100 w-12 align-middle h-full" }), /* @__PURE__ */ jsx(TableCell, { className: "p-0 min-w-[120px] align-top h-full" }), /* @__PURE__ */ jsx(TableCell, { colSpan: onDelete ? 3 : 2, className: "text-center p-0", children: /* @__PURE__ */ jsx( Button, { variant: "ghost", size: "sm", onClick: handleAdd, className: "h-9 w-9 text-blue-600 border-blue-200 hover:bg-transparent cursor-pointer", children: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }) } ) }) ] }) ] }) }) }); } // src/extracted-data/property-renderer/property-renderer-utils.ts var isPropertyChanged = (changedPaths, keyPath) => { if (!changedPaths) return false; const pathString = keyPath.join("."); return changedPaths.has(pathString); }; var isArrayOfObjects = (value) => { return value.length > 0 && typeof value[0] === "object" && value[0] !== null && !Array.isArray(value[0]); }; var shouldShowKeyOnSeparateLine = (value) => { return Array.isArray(value) && isArrayOfObjects(value) || typeof value === "object" && value !== null && !Array.isArray(value); }; function PropertyRenderer({ keyPath, value, onUpdate, changedPaths, metadata, validationErrors = [], onClickField, editable = true }) { var _a, _b; const pathString = keyPath.join("."); const isChanged = isPropertyChanged(changedPaths, keyPath); const effectiveMetadata = { schema: (_a = metadata == null ? void 0 : metadata.schema) != null ? _a : {}, extracted: (_b = metadata == null ? void 0 : metadata.extracted) != null ? _b : {} }; const getMetadata = (path) => { if (effectiveMetadata.extracted) { return findExtractedFieldMetadata(path, effectiveMetadata.extracted); } return void 0; }; const handleFieldClick = (args) => { onClickField == null ? void 0 : onClickField({ value: args.value, metadata: args.metadata, path: keyPath }); }; const renderFieldLabel = (key, currentKeyPath, additionalClasses) => { const fieldInfo2 = getFieldDisplayInfo( key, effectiveMetadata.schema, validationErrors, currentKeyPath ); const baseClasses = getFieldLabelClasses(fieldInfo2); const finalClasses = additionalClasses ? `${baseClasses} ${additionalClasses}` : baseClasses; return /* @__PURE__ */ jsx("div", { className: finalClasses, children: getFieldLabelText(fieldInfo2) }); }; if (value === null || value === void 0) { const fieldInfo2 = findFieldSchemaMetadata( keyPath, effectiveMetadata.schema ); const expectedType2 = (fieldInfo2 == null ? void 0 : fieldInfo2.schemaType) ? toPrimitiveType(fieldInfo2.schemaType) : "string" /* STRING */; const isRequired2 = (fieldInfo2 == null ? void 0 : fieldInfo2.isRequired) || false; return /* @__PURE__ */ jsx( EditableField, { value: "N/A", onSave: (newValue) => onUpdate(keyPath, newValue), metadata: getMetadata(pathString), isChanged, expectedType: expectedType2, required: isRequired2, onClick: handleFieldClick, editable } ); } if (Array.isArray(value)) { if (value.length === 0) { return /* @__PURE__ */ jsx( ListRenderer, { data: value, onUpdate: () => { }, onAdd: (newValue) => { const newArray = [newValue]; const newItemPath = [...keyPath, "0"]; onUpdate(keyPath, newArray, [newItemPath]); }, changedPaths, keyPath, metadata: { schema: effectiveMetadata.schema, extracted: {} }, editable } ); } if (isArrayOfObjects(value)) { return /* @__PURE__ */ jsx( TableRenderer, { data: value, onUpdate: (index, key, newValue, affectedPaths) => { const newArray = [...value]; newArray[index] = __spreadProps(__spreadValues({}, newArray[index]), { [key]: newValue }); if (affectedPaths && affectedPaths.length > 0) { const absolutePaths = affectedPaths.map((path) => { const pathParts = path.split("."); return [...keyPath, ...pathParts]; }); onUpdate(keyPath, newArray, absolutePaths); } else { const cellPath = [...keyPath, String(index), key]; onUpdate(keyPath, newArray, [cellPath]); } }, onAddRow: (newRow) => { const newArray = [ ...value, newRow ]; const newRowPath = [...keyPath, String(value.length)]; onUpdate(keyPath, newArray, [newRowPath]); }, onDeleteRow: (index) => { const newArray = value.filter((_, i) => i !== index); onUpdate(keyPath, newArray, [keyPath]); }, changedPaths, keyPath, metadata: { schema: effectiveMetadata.schema, extracted: effectiveMetadata.extracted }, validationErrors, onClickField, editable } ); } else { return /* @__PURE__ */ jsx( ListRenderer, { data: value, onUpdate: (index, newValue) => { const newArray = [...value]; newArray[index] = newValue; const itemPath = [...keyPath, String(index)]; onUpdate(keyPath, newArray, [itemPath]); }, onAdd: (newValue) => { const newArray = [...value, newValue]; const newItemPath = [...keyPath, String(value.length)]; onUpdate(keyPath, newArray, [newItemPath]); }, onDelete: (index) => { const newArray = value.filter((_, i) => i !== index); onUpdate(keyPath, newArray, [keyPath]); }, changedPaths, keyPath, metadata: { schema: effectiveMetadata.schema, extracted: effectiveMetadata.extracted }, onClickField, editable } ); } } if (typeof value === "object") { return /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx("div", { className: "pl-4 border-l-2 border-gray-200", children: Object.entries(value).map(([key, val]) => { if (shouldShowKeyOnSeparateLine(val)) { return /* @__PURE__ */ jsxs("div", { children: [ renderFieldLabel(key, [...keyPath, key]), /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx( PropertyRenderer, { keyPath: [...keyPath, key], value: val, onUpdate, changedPaths, metadata: effectiveMetadata, validationErrors, onClickField, editable } ) }) ] }, key); } else { return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-6", children: [ renderFieldLabel( key, [...keyPath, key], "min-w-0 flex-shrink-0" ), /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx( PropertyRenderer, { keyPath: [...keyPath, key], value: val, onUpdate, changedPaths, metadata: effectiveMetadata, validationErrors, onClickField, editable } ) }) ] }) }, key); } }) }) }); } const fieldInfo = findFieldSchemaMetadata(keyPath, effectiveMetadata.schema); const expectedType = (fieldInfo == null ? void 0 : fieldInfo.schemaType) ? toPrimitiveType(fieldInfo.schemaType) : "string" /* STRING */; const isRequired = (fieldInfo == null ? void 0 : fieldInfo.isRequired) || false; return /* @__PURE__ */ jsx( EditableField, { value, onSave: (newValue) => onUpdate(keyPath, newValue), metadata: getMetadata(pathString), isChanged, expectedType, required: isRequired, onClick: handleFieldClick, editable } ); } // src/extracted-data/schema-reconciliation.ts function reconcileDataWithJsonSchema(originalData, jsonSchema) { const metadata = {}; const requiredFields = /* @__PURE__ */ new Set(); const addedOptionalFields = /* @__PURE__ */ new Set(); const validationErrors = []; const data = structuredClone(originalData); fillMissingFieldsFromJsonSchema(data, jsonSchema, [], { metadata, requiredFields, addedOptionalFields}); return { data, schemaMetadata: metadata, requiredFields, addedOptionalFields, validationErrors, isValid: validationErrors.length === 0 }; } function fillMissingFieldsFromJsonSchema(data, jsonSchema, currentPath, context) { var _a, _b; const properties = jsonSchema.properties || {}; if (data === null || data === void 0 || typeof data !== "object" || Array.isArray(data)) { return; } const dataObj = data; for (const [fieldName, fieldSchema] of Object.entries(properties)) { const fieldPath = [...currentPath, fieldName]; const pathString = fieldPath.join("."); const existsInData = fieldName in dataObj; const isRequired = (_b = ((_a = jsonSchema.required) == null ? void 0 : _a.includes(fieldName)) && !isNullable(fieldSchema)) != null ? _b : false; let wasMissing = false; if (!existsInData && !isRequired) { dataObj[fieldName] = void 0; wasMissing = true; context.addedOptionalFields.add(pathString); } const baseSchema = fieldSchema; const metadata = { isRequired, isOptional: !isRequired, schemaType: baseSchema.type || "unknown", title: baseSchema.title, description: baseSchema.description, wasMissing }; context.metadata[pathString] = metadata; if (baseSchema.type === "array") { const arraySchema = baseSchema; if (arraySchema.items && typeof arraySchema.items === "object" && !Array.isArray(arraySchema.items)) { const itemSchema = arraySchema.items; if (itemSchema.type === "object") { const objectItemSchema = itemSchema; generateArrayItemMetadata( objectItemSchema, [...fieldPath, "*"], // Add "*" wildcard for array items context ); } else { const itemPathString = [...fieldPath, "*"].join("."); const itemMetadata = { isRequired: false, // Array items themselves are not required isOptional: true, schemaType: itemSchema.type || "unknown", title: itemSchema.title, description: itemSchema.description, wasMissing: false }; context.metadata[itemPathString] = itemMetadata; } } } if (isRequired) { context.requiredFields.add(pathString); } if (baseSchema.type === "object" && dataObj[fieldName] !== null && dataObj[fieldName] !== void 0) { const objectSchema = baseSchema; fillMissingFieldsFromJsonSchema( dataObj[fieldName], objectSchema, fieldPath, context ); } else if (baseSchema.type === "array" && Array.isArray(dataObj[fieldName])) { const arraySchema = baseSchema; if (arraySchema.items && typeof arraySchema.items === "object" && !Array.isArray(arraySchema.items)) { const itemSchema = arraySchema.items; if (itemSchema.type === "object") { dataObj[fieldName].forEach((item, index) => { fillMissingFieldsFromJsonSchema( item, itemSchema, [...fieldPath, String(index)], context ); }); } } } } } function generateArrayItemMetadata(objectSchema, currentPath, context) { var _a, _b; const { properties } = objectSchema; if (!properties || typeof properties !== "object") { return; } for (const [fieldName, fieldSchema] of Object.entries(properties)) { const fieldPath = [...currentPath, fieldName]; const pathString = fieldPath.join("."); const baseSchema = fieldSchema; const isRequired = (_b = (_a = objectSchema.required) == null ? void 0 : _a.includes(fieldName)) != null ? _b : false; const metadata = { isRequired, isOptional: !isRequired, schemaType: baseSchema.type || "unknown", title: baseSchema.title, description: baseSchema.description, wasMissing: false // Array item fields are schema-defined, not missing }; context.metadata[pathString] = metadata; if (baseSchema.type === "object") { const nestedObjectSchema = baseSchema; generateArrayItemMetadata(nestedObjectSchema, fieldPath, context); } if (baseSchema.type === "array") { const arraySchema = baseSchema; if (arraySchema.items && typeof arraySchema.items === "object" && !Array.isArray(arraySchema.items)) { const itemSchema = arraySchema.items; if (itemSchema.type === "object") { const objectItemSchema = itemSchema; generateArrayItemMetadata( objectItemSchema, [...fieldPath, "*"], context ); } else { const itemPathString = [...fieldPath, "*"].join("."); const itemMetadata = { isRequired: false, isOptional: true, schemaType: itemSchema.type || "unknown", title: itemSchema.title, description: itemSchema.description, wasMissing: false }; context.metadata[itemPathString] = itemMetadata; } } } } } function ExtractedDataDisplay({ extractedData, emptyMessage = "No extracted data available", onChange, editable = true, jsonSchema, onClickField }) { const [changedPaths, setChangedPaths] = useState(/* @__PURE__ */ new Set()); const { data, field_metadata } = extractedData; const reconciliationResult = useMemo(() => { if (!jsonSchema) { return null; } return reconcileDataWithJsonSchema(data, jsonSchema); }, [data, jsonSchema]); const displayData = (reconciliationResult == null ? void 0 : reconciliationResult.data) || data; const schemaMetadata = (reconciliationResult == null ? void 0 : reconciliationResult.schemaMetadata) || {}; const validationErrors = (reconciliationResult == null ? void 0 : reconciliationResult.validationErrors) || []; const renderFieldLabel = (key, additionalClasses) => { const fieldInfo = getFieldDisplayInfo( key, schemaMetadata, validationErrors, [key] ); const baseClasses = getFieldLabelClasses(fieldInfo); const finalClasses = additionalClasses ? `${baseClasses} ${additionalClasses}` : baseClasses; return /* @__PURE__ */ jsx("div", { className: finalClasses, children: getFieldLabelText(fieldInfo) }); }; if (!displayData || Object.keys(displayData).length === 0) { return /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500 text-center py-4", children: emptyMessage }); } const handleUpdate2 = (path, newValue, additionalPaths) => { if (!editable || !onChange) return; const newData = __spreadValues({}, data); let current = newData; for (let i = 0; i < path.length - 1; i++) { current = current[path[i]]; } current[path[path.length - 1]] = newValue; const pathsToTrack = [path, ...additionalPaths || []]; setChangedPaths((prev) => { const newSet = new Set(prev); pathsToTrack.forEach((p) => { newSet.add(p.join(".")); }); return newSet; }); onChange(newData); }; return /* @__PURE__ */ jsx("div", { children: Object.keys((jsonSchema == null ? void 0 : jsonSchema.properties) || {}).map((key) => { const value = displayData[key]; if (Array.isArray(value) && value.length > 0 && typeof value[0] === "object" && value[0] !== null) { return /* @__PURE__ */ jsxs("div", { children: [ renderFieldLabel(key), /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx( PropertyRenderer, { keyPath: [key], value, onUpdate: handleUpdate2, changedPaths, metadata: { schema: schemaMetadata, extracted: field_metadata }, validationErrors, onClickField, editable } ) }) ] }, key); } else if (typeof value === "object" && value !== null) { return /* @__PURE__ */ jsxs("div", { children: [ renderFieldLabel(key), /* @__PURE__ */ jsx( PropertyRenderer, { keyPath: [key], value, onUpdate: handleUpdate2, changedPaths, metadata: { schema: schemaMetadata, extracted: field_metadata }, validationErrors, onClickField, editable } ) ] }, key); } else { return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-6"