import { useState, useEffect } from "react";
import type { FileDetail as FileDetailType, FileVersionSummary, FileVersion } from "../../lib/api";
import { BodyRenderer } from "./BodyRenderer";
import { VersionHistoryDropdown } from "./VersionHistoryDropdown";
export interface FocusedElement {
index: number;
type: string;
preview: string;
}
interface FileDetailProps {
file: FileDetailType;
loading: boolean;
onBack: () => void;
onSave: (id: string, name: string, description: string) => void;
onDelete: (id: string) => void;
onBodyElementUpdate?: (index: number, element: import("../../lib/api").BodyElement) => void;
onBodyReorder?: (fromIndex: number, toIndex: number) => void;
onBodyElementDelete?: (index: number) => void;
onBodyElementDuplicate?: (index: number) => void;
onEditingChange?: (isEditing: boolean) => void;
hasPendingRemoteUpdate?: boolean;
onOverwrite?: () => void;
// Focus element props
focusedElement?: FocusedElement | null;
onFocusElement?: (element: FocusedElement | null) => void;
onGenerateFromElement?: (index: number, action: string) => void;
onConvertElement?: (index: number, toType: string) => void;
onCreateTaskFromElement?: (index: number, selectedText?: string) => void;
// Version history props
versions?: FileVersionSummary[];
versionsLoading?: boolean;
selectedVersion?: FileVersion | null;
loadingVersion?: boolean;
restoring?: boolean;
onSelectVersion?: (version: number) => void;
onRestoreVersion?: (version: number) => void;
onClearVersionSelection?: () => void;
}
export function FileDetail({
file,
loading,
onBack,
onSave,
onDelete,
onBodyElementUpdate,
onBodyReorder,
onBodyElementDelete,
onBodyElementDuplicate,
onEditingChange,
hasPendingRemoteUpdate,
onOverwrite,
focusedElement: _focusedElement,
onFocusElement,
onGenerateFromElement,
onConvertElement,
onCreateTaskFromElement,
versions = [],
versionsLoading = false,
selectedVersion = null,
loadingVersion = false,
restoring = false,
onSelectVersion,
onRestoreVersion,
onClearVersionSelection,
}: FileDetailProps) {
const [isEditing, setIsEditing] = useState(false);
const [name, setName] = useState(file.name);
const [description, setDescription] = useState(file.description || "");
const [transcriptExpanded, setTranscriptExpanded] = useState(false);
// Helper to get element preview text
const getElementPreview = (index: number): string => {
const element = file.body[index];
if (!element) return "";
switch (element.type) {
case "heading":
case "paragraph":
return element.text.slice(0, 50) + (element.text.length > 50 ? "..." : "");
case "code":
return element.content.slice(0, 50) + (element.content.length > 50 ? "..." : "");
case "list":
return element.items[0]?.slice(0, 40) + (element.items.length > 1 ? ` (+${element.items.length - 1} more)` : "");
case "chart":
return element.title || `${element.chartType} chart`;
case "image":
return element.alt || element.caption || "Image";
case "markdown":
return element.content.slice(0, 50) + (element.content.length > 50 ? "..." : "");
default:
return "Element";
}
};
// Handler for focus action from context menu
const handleFocusElement = (index: number) => {
const element = file.body[index];
if (!element || !onFocusElement) return;
onFocusElement({
index,
type: element.type,
preview: getElementPreview(index),
});
};
// Update local state when file changes
useEffect(() => {
setName(file.name);
setDescription(file.description || "");
}, [file.name, file.description]);
const handleSave = () => {
onSave(file.id, name, description);
setIsEditing(false);
};
const handleCancel = () => {
setName(file.name);
setDescription(file.description || "");
setIsEditing(false);
};
if (loading) {
return (
<div className="panel h-full flex items-center justify-center">
<div className="font-mono text-[#9bc3ff] text-sm">Loading...</div>
</div>
);
}
return (
<div className="panel h-full flex flex-col">
{/* Header */}
<div className="p-4 border-b border-dashed border-[rgba(117,170,252,0.35)]">
<div className="flex items-center justify-between mb-3">
<button
onClick={onBack}
className="font-mono text-xs text-[#75aafc] hover:text-[#9bc3ff] transition-colors"
>
← Back to list
</button>
<div className="flex items-center gap-2">
{isEditing ? (
<>
{onSelectVersion && onRestoreVersion && onClearVersionSelection && (
<VersionHistoryDropdown
currentVersion={file.version}
versions={versions}
loading={versionsLoading}
selectedVersion={selectedVersion}
loadingVersion={loadingVersion}
onSelectVersion={onSelectVersion}
onRestoreVersion={onRestoreVersion}
onClearSelection={onClearVersionSelection}
restoring={restoring}
/>
)}
<button
onClick={handleCancel}
className="px-3 py-1.5 font-mono text-xs text-[#9bc3ff] border border-[rgba(117,170,252,0.25)] hover:border-[#3f6fb3] transition-colors uppercase"
>
Cancel
</button>
<button
onClick={handleSave}
className="px-3 py-1.5 font-mono text-xs text-[#dbe7ff] bg-[#0f3c78] border border-[#3f6fb3] hover:bg-[#153667] transition-colors uppercase"
>
Save
</button>
</>
) : (
<>
<button
onClick={() => setIsEditing(true)}
className="px-3 py-1.5 font-mono text-xs text-[#dbe7ff] border border-[#0f3c78] hover:border-[#3f6fb3] transition-colors uppercase"
>
Edit
</button>
<button
onClick={() => onDelete(file.id)}
className="px-3 py-1.5 font-mono text-xs text-red-400 border border-red-400/30 hover:border-red-400/50 transition-colors uppercase"
>
Delete
</button>
</>
)}
</div>
</div>
{isEditing ? (
<div className="space-y-3">
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc]"
placeholder="File name"
/>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
className="w-full px-3 py-2 bg-[#0d1b2d] border border-[#3f6fb3] text-[#dbe7ff] font-mono text-sm focus:outline-none focus:border-[#75aafc] resize-none"
rows={2}
placeholder="Description (optional)"
/>
</div>
) : (
<>
<h2 className="font-mono text-lg text-[#dbe7ff] mb-1">
{file.name}
</h2>
{file.description && (
<p className="font-mono text-sm text-[#9bc3ff]">
{file.description}
</p>
)}
</>
)}
</div>
{/* Content */}
<div className="flex-1 overflow-y-auto p-4 space-y-6">
{/* Summary Section */}
{file.summary && (
<div className="border-l-2 border-[#9bc3ff] pl-4">
<h3 className="font-mono text-xs text-[#75aafc] uppercase mb-2">
Summary
</h3>
<p className="font-mono text-sm text-[#dbe7ff] leading-relaxed">
{file.summary}
</p>
</div>
)}
{/* Body Content */}
<div>
<h3 className="font-mono text-xs text-[#75aafc] uppercase mb-3">
Content
</h3>
<BodyRenderer
elements={file.body}
isEditing={isEditing}
onUpdate={onBodyElementUpdate}
onReorder={onBodyReorder}
onEditingChange={onEditingChange}
hasPendingRemoteUpdate={hasPendingRemoteUpdate}
onOverwrite={onOverwrite}
onFocusElement={handleFocusElement}
onDeleteElement={onBodyElementDelete}
onDuplicateElement={onBodyElementDuplicate}
onConvertElement={onConvertElement}
onGenerateFromElement={onGenerateFromElement}
onCreateTaskFromElement={onCreateTaskFromElement}
/>
</div>
{/* Collapsible Transcript Section - only show if there are entries */}
{file.transcript.length > 0 && (
<div className="border-t border-dashed border-[rgba(117,170,252,0.35)] pt-4">
<button
onClick={() => setTranscriptExpanded(!transcriptExpanded)}
className="flex items-center gap-2 font-mono text-xs text-[#75aafc] hover:text-[#9bc3ff] transition-colors uppercase w-full text-left"
>
<span
className={`transition-transform ${
transcriptExpanded ? "rotate-90" : ""
}`}
>
>
</span>
Transcript ({file.transcript.length} entries)
</button>
{transcriptExpanded && (
<div className="mt-4 space-y-3 pl-4">
{file.transcript.map((entry) => (
<div key={entry.id} className="font-mono text-sm">
<div className="flex items-baseline gap-2 mb-1">
<span className="text-[#75aafc] text-xs">
[{entry.start.toFixed(2)}s - {entry.end.toFixed(2)}s]
</span>
<span className="text-[#9bc3ff] text-xs font-bold">
{entry.speaker}
</span>
</div>
<p className="m-0 text-[#dbe7ff] leading-relaxed">
{entry.text}
</p>
</div>
))}
</div>
)}
</div>
)}
</div>
</div>
);
}