diff --git a/frontend/src/components/InspectorPanel.tsx b/frontend/src/components/InspectorPanel.tsx index 07d77dd..925e900 100644 --- a/frontend/src/components/InspectorPanel.tsx +++ b/frontend/src/components/InspectorPanel.tsx @@ -123,7 +123,10 @@ export const InspectorPanel: React.FC = (p) => { }`} > {React.isValidElement(t.icon) ? React.cloneElement(t.icon as React.ReactElement<{ size?: number }>, { size: 18 }) : t.icon} - {t.id === 'notes' && p.annotations.length > 0 && ( + {t.id === 'notes' && p.annotations.some((a) => a.type !== 'widget') && ( + + )} + {t.id === 'forms' && p.annotations.some((a) => a.type === 'widget') && ( )} @@ -134,22 +137,18 @@ export const InspectorPanel: React.FC = (p) => { {/* Body */}
{p.activeTab === 'pages' && } - {p.activeTab === 'notes' && } + {p.activeTab === 'notes' && a.type !== 'widget')} onNavigate={p.onNavigateAnnotation} onDelete={p.onDeleteAnnotation} />} {p.activeTab === 'search' && } {p.activeTab === 'properties' && } {p.activeTab === 'fonts' && } {p.activeTab === 'outline' && } - {p.activeTab === 'forms' && ( - } title="Form fields" - hint="Viewing and filling AcroForm fields is on the engine roadmap and will light up here." /> - )} + {p.activeTab === 'forms' && a.type === 'widget')} onNavigate={p.onNavigateAnnotation} />}
); }; -/* ----------------------------------------------------------------- Pages */ const PagesTab: React.FC = (p) => { const [draggedIdx, setDraggedIdx] = React.useState(null); @@ -192,7 +191,6 @@ const PagesTab: React.FC = (p) => { ); }; -/* ----------------------------------------------------------------- Notes */ const NotesTab: React.FC<{ annotations: Annotation[]; onNavigate: (a: Annotation) => void; onDelete: (a: Annotation) => void }> = ({ annotations, onNavigate, onDelete }) => { if (annotations.length === 0) { return } title="No annotations yet" @@ -225,7 +223,6 @@ const NotesTab: React.FC<{ annotations: Annotation[]; onNavigate: (a: Annotation ); }; -/* --------------------------------------------------------------- Outline */ const OutlineTab: React.FC<{ outline: OutlineItem[]; onNavigate: (pageIndex: number) => void }> = ({ outline, onNavigate }) => { if (outline.length === 0) { return } title="No outline" hint="This document has no bookmarks/outline." />; @@ -249,7 +246,6 @@ const OutlineTab: React.FC<{ outline: OutlineItem[]; onNavigate: (pageIndex: num ); }; -/* ---------------------------------------------------------------- Search */ const SearchTab: React.FC = (p) => { return (
@@ -342,7 +338,6 @@ const SearchTab: React.FC = (p) => { ); }; -/* ------------------------------------------------------------ Properties */ const PropertiesTab: React.FC<{ metadata: DocumentMetadata | null; sizeBytes?: number; totalPages: number; filename?: string }> = ({ metadata, sizeBytes, totalPages, filename }) => { const rows: [string, string | undefined][] = [ ['File name', filename], @@ -370,12 +365,10 @@ const PropertiesTab: React.FC<{ metadata: DocumentMetadata | null; sizeBytes?: n ); }; -/* ----------------------------------------------------------------- Fonts */ const FontsTab: React.FC<{ fonts: FontInfo[] }> = ({ fonts }) => { if (fonts.length === 0) { return } title="No font data" hint="Font inventory appears once a document with embedded fonts is open." />; } - // De-duplicate by name const seen = new Set(); const unique = fonts.filter((f) => (seen.has(f.fontName) ? false : (seen.add(f.fontName), true))); return ( @@ -408,3 +401,34 @@ const Badge: React.FC<{ label: string; ok?: boolean; warn?: boolean }> = ({ labe {label} ); + +const FIELD_TYPE_LABEL: Record = { Tx: 'Text', Btn: 'Button', Ch: 'Choice', Sig: 'Signature' }; + +const FormsTab: React.FC<{ fields: Annotation[]; onNavigate: (a: Annotation) => void }> = ({ fields, onNavigate }) => { + if (fields.length === 0) { + return } title="No form fields" hint="This document has no AcroForm fields." />; + } + return ( +
+

{fields.length} field{fields.length > 1 ? 's' : ''}

+ {fields.map((f) => ( + onNavigate(f)} + className="flex flex-col gap-1.5 rounded-[8px] border border-[#ebedf0] bg-[#f6f7f9] p-2.5 text-left transition-colors hover:border-[#dadde2]"> +
+ {f.fieldName || '(unnamed field)'} + {FIELD_TYPE_LABEL[f.fieldType || ''] || f.fieldType || 'Field'} +
+
+ Value + {f.fieldValue && f.fieldValue.trim() ? f.fieldValue : } + {f.pageIndex !== undefined && p.{f.pageIndex + 1}} +
+ {f.fieldOptions && f.fieldOptions.length > 0 && ( + Options: {f.fieldOptions.slice(0, 5).join(', ')}{f.fieldOptions.length > 5 ? '…' : ''} + )} +
+ ))} +

Form fields are view-only — filling is on the Phase 3 roadmap.

+
+ ); +}; \ No newline at end of file