Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/__tests__/integration/component-integration.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,11 @@ describe('Component Integration Tests', () => {
/>
)

// Should show progress indicator
expect(screen.getByText(/Generating Formats \(/)).toBeInTheDocument()
expect(screen.getByText(/1\/3/)).toBeInTheDocument()
// Should show progress indicator (bullets complete, so showing format 2 of 3)
expect(screen.getByText(/Generating Formats \(Format 2 of 3\)/)).toBeInTheDocument()

// Should show currently processing format
expect(screen.getByText(/Currently processing: paragraphs/)).toBeInTheDocument()
expect(screen.getByText(/Currently processing:/)).toBeInTheDocument()
})

it('should handle clear functionality', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/input/ExampleContentMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export const ExampleContentMenu = ({
aria-controls={isOpen ? menuId : undefined}
>
<svg
className="h-5 w-5"
className="h-5 w-5 text-current"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
Expand All @@ -163,7 +163,7 @@ export const ExampleContentMenu = ({
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
<span>Load example</span>
<span className="text-current">Load example</span>
<svg
className={`h-4 w-4 transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}
fill="none"
Expand Down
37 changes: 22 additions & 15 deletions src/components/input/TextInputPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface TextInputPanelProps {
onProcessingError?: (error: Error) => void
initialText?: string
selectedFormats?: FormatType[]
targetLanguage?: string
onInputStateChange?: (hasText: boolean) => void
onProcessingStart?: () => void
onOperationProgress?: (progress: OperationProgress | null) => void
Expand All @@ -41,6 +42,7 @@ export const TextInputPanel = ({
onProcessingError,
initialText = '',
selectedFormats: propSelectedFormats,
targetLanguage,
onInputStateChange,
onProcessingStart,
onOperationProgress,
Expand Down Expand Up @@ -97,9 +99,12 @@ export const TextInputPanel = ({
onInputStateChange?.(text.trim().length > 0)
}, [text, onInputStateChange])

const preferenceSnapshot = loadPreferences()

// Format transform hook - auto-initialize with reading level
const formatTransform = useFormatTransform(true, {
readingLevel: loadPreferences().readingLevel || undefined,
readingLevel: preferenceSnapshot.readingLevel || undefined,
targetLanguage: targetLanguage || preferenceSnapshot.targetLanguage || undefined,
})

// Pass progress updates to parent
Expand Down Expand Up @@ -203,13 +208,18 @@ export const TextInputPanel = ({

// Store the original text before processing
const originalTextSnapshot = text
const preferences = loadPreferences()
const readingLevelPreference = preferences.readingLevel || undefined
const effectiveTargetLanguage =
targetLanguage || preferences.targetLanguage || undefined

try {
// Check if format transform service is ready
if (!formatTransform.isReady) {
setStatus('initializing')
await formatTransform.initialize({
readingLevel: loadPreferences().readingLevel || undefined,
readingLevel: readingLevelPreference,
targetLanguage: effectiveTargetLanguage,
})
}

Expand All @@ -223,7 +233,8 @@ export const TextInputPanel = ({
text,
selectedFormats[0],
{
readingLevel: loadPreferences().readingLevel || undefined,
readingLevel: readingLevelPreference,
targetLanguage: effectiveTargetLanguage,
}
)

Expand All @@ -238,18 +249,15 @@ export const TextInputPanel = ({
text,
selectedFormats,
(format, content, isComplete) => {
console.log('[TextInputPanel] Streaming callback:', format, 'isComplete:', isComplete, 'content length:', content.length)
if (isComplete) {
aggregatedResults[format] = content
console.log('[TextInputPanel] ✅ Format SAVED to aggregatedResults:', format, 'Content length:', content.length)
} else {
console.log('[TextInputPanel] ⏳ Format streaming (not saved yet):', format)
}
// Pass streaming updates to parent
onStreamingUpdate?.(format, content, isComplete)
},
{
readingLevel: loadPreferences().readingLevel || undefined,
readingLevel: readingLevelPreference,
targetLanguage: effectiveTargetLanguage,
}
)

Expand Down Expand Up @@ -506,13 +514,12 @@ export const TextInputPanel = ({
<div className="ml-auto flex flex-col items-end gap-1">
<div className="flex items-center gap-2 text-sm text-neutral-600">
<span
className={`h-2.5 w-2.5 rounded-full ${
formatTransform.availability === 'available' || formatTransform.availability === 'readily'
? 'bg-green-500'
: formatTransform.availability === 'downloadable'
? 'bg-yellow-500 animate-pulse'
: 'bg-red-500'
}`}
className={`h-2.5 w-2.5 rounded-full ${formatTransform.availability === 'available' || formatTransform.availability === 'readily'
? 'bg-green-500'
: formatTransform.availability === 'downloadable'
? 'bg-yellow-500 animate-pulse'
: 'bg-red-500'
}`}
aria-hidden="true"
/>
<span>
Expand Down
9 changes: 3 additions & 6 deletions src/components/output/FormatSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,10 @@ const FormatSectionComponent = ({
onToggle,
onCopy,
}: FormatSectionProps) => {
console.log(`[FormatSection ${format}] RENDER - content length: ${content?.length || 0}, isLoading: ${isLoading}, isExpanded: ${isExpanded}`)

const [copySuccess, setCopySuccess] = useState(false)
const formatConfig = useMemo(() => getFormatConfig(format), [format])

console.log(`[FormatSection ${format}] formatConfig:`, formatConfig ? 'found' : 'NOT FOUND')

const handleCopy = useCallback(async () => {
onCopy()
Expand Down Expand Up @@ -130,7 +128,7 @@ const FormatSectionComponent = ({
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 714 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 0 1 4 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
<span className="font-medium">Processing...</span>
Expand All @@ -155,9 +153,8 @@ const FormatSectionComponent = ({
</div>
) : (
<svg
className={`h-5 w-5 text-neutral-400 transition-transform dark:text-neutral-500 ${
isExpanded ? 'rotate-180' : ''
}`}
className={`h-5 w-5 text-neutral-400 transition-transform dark:text-neutral-500 ${isExpanded ? 'rotate-180' : ''
}`}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
Expand Down
3 changes: 2 additions & 1 deletion src/components/output/FormattedOutput.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ describe('FormattedOutput', () => {
isLoading={isLoading}
/>
)
expect(screen.getByText(/1\/3/)).toBeInTheDocument()
// 1 complete, so showing Format 2 of 3
expect(screen.getByText(/Format 2 of 3/)).toBeInTheDocument()
})

it('should show currently processing format', () => {
Expand Down
13 changes: 8 additions & 5 deletions src/components/output/FormattedOutput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,21 +161,26 @@ export const FormattedOutput = ({

const hasLoadingInfo = Boolean(isLoading && isLoading.size > 0)
const isInProgress = hasLoadingInfo && completed < total
const percent = total > 0 ? (completed / total) * 100 : 0
const partialStep = isInProgress && active ? 0.5 : 0
const percent = total > 0 ? ((completed + partialStep) / total) * 100 : 0
const currentPosition = isInProgress
? Math.min(completed + 1, total)
: total

return {
total,
completed,
active: isInProgress ? active : undefined,
isInProgress,
currentPosition,
percent,
}
}, [formats, isLoading, results])

const formatCountLabel = useMemo(() => {
const suffix = progress.total !== 1 ? 's' : ''
if (progress.isInProgress) {
return `${progress.completed} of ${progress.total} format${suffix} complete`
return `Currently processing format ${progress.currentPosition} of ${progress.total}`
}
return `${progress.total} format${suffix} generated`
}, [progress])
Expand Down Expand Up @@ -207,7 +212,7 @@ export const FormattedOutput = ({
</svg>
<div className="flex-1">
<p className="font-semibold text-blue-900 dark:text-blue-200">
Generating Formats ({progress.completed}/{progress.total})
Generating Formats (Format {progress.currentPosition} of {progress.total})
</p>
<p className="mt-1 text-sm text-blue-800 dark:text-blue-200/80">
{progress.active && `Currently processing: ${getFormatLabels([progress.active])}`}
Expand Down Expand Up @@ -404,8 +409,6 @@ export const FormattedOutput = ({
const isFormatLoading = isLoading?.get(format) ?? false
const isFormatExpanded = expandedFormats.has(format)

console.log(`[FormattedOutput RENDER FormatSection] format: ${format}, content length: ${content.length}, isLoading: ${isFormatLoading}, isExpanded: ${isFormatExpanded}`)

return (
<FormatSection
key={format}
Expand Down
20 changes: 11 additions & 9 deletions src/components/preferences/FormatPresetSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ export const FormatPresetSelector = ({
<div className="grid gap-4">
<div className="flex items-center justify-between">
<div>
<h3 className="text-sm font-semibold text-neutral-900">Quick Presets</h3>
<p className="text-xs text-neutral-600">Common format combinations</p>
<h3 className="text-sm font-semibold text-neutral-900 dark:text-neutral-100">Quick Presets</h3>
<p className="text-xs text-neutral-600 dark:text-neutral-400">Common format combinations</p>
</div>
{selectedPresetId && (
<button
type="button"
onClick={() => onSelectPreset(null)}
disabled={disabled}
className="text-xs font-medium text-synapse-600 transition hover:text-synapse-700 disabled:cursor-not-allowed disabled:opacity-50"
className="text-xs font-medium text-synapse-600 transition hover:text-synapse-700 disabled:cursor-not-allowed disabled:opacity-50 dark:text-synapse-300 dark:hover:text-synapse-200"
>
Clear preset
</button>
Expand All @@ -78,8 +78,8 @@ export const FormatPresetSelector = ({
disabled={disabled}
className={`rounded-xl border px-4 py-3 text-left transition hc-surface ${
isSelected
? 'border-synapse-500 bg-synapse-50 shadow-soft hc-surface--active'
: 'border-neutral-200 hover:border-neutral-300 hover:bg-neutral-50'
? 'border-synapse-500 bg-synapse-50 shadow-soft hc-surface--active dark:border-synapse-400 dark:bg-synapse-900/30'
: 'border-neutral-200 hover:border-neutral-300 hover:bg-neutral-50 dark:border-neutral-700 dark:hover:border-neutral-500 dark:hover:bg-neutral-800/60'
} ${disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'}`}
aria-pressed={isSelected}
>
Expand All @@ -88,7 +88,9 @@ export const FormatPresetSelector = ({
<div className="mt-0.5 flex-shrink-0">
<svg
className={`h-5 w-5 ${
isSelected ? 'text-synapse-600 hc-icon-accent' : 'text-neutral-500'
isSelected
? 'text-synapse-600 hc-icon-accent dark:text-synapse-200'
: 'text-neutral-500 dark:text-neutral-400'
}`}
fill="none"
viewBox="0 0 24 24"
Expand All @@ -100,14 +102,14 @@ export const FormatPresetSelector = ({
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<h4 className="text-sm font-semibold text-neutral-900">{preset.label}</h4>
<h4 className="text-sm font-semibold text-neutral-900 dark:text-neutral-100">{preset.label}</h4>
{isSelected && (
<span className="rounded-full bg-synapse-100 px-2 py-0.5 text-xs font-medium text-synapse-700 hc-chip hc-chip--fill">
<span className="rounded-full bg-synapse-100 px-2 py-0.5 text-xs font-medium text-synapse-700 hc-chip hc-chip--fill dark:bg-synapse-900/50 dark:text-synapse-200">
Active
</span>
)}
</div>
<p className="mt-1 text-xs text-neutral-600">{preset.description}</p>
<p className="mt-1 text-xs text-neutral-600 dark:text-neutral-400">{preset.description}</p>
</div>
</div>
</button>
Expand Down
24 changes: 13 additions & 11 deletions src/components/preferences/FormatPreviewCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ export const FormatPreviewCard = ({
disabled={disabled}
className={`flex h-full flex-col items-start rounded-2xl border px-5 py-4 text-left transition hc-surface ${
isSelected
? 'border-primary-600 bg-primary-50 shadow-[0_0_0_4px_rgba(59,130,246,0.15)] hc-surface--active'
: 'border-neutral-200 hover:border-neutral-300 hover:bg-neutral-50'
? 'border-primary-600 bg-primary-50 shadow-[0_0_0_4px_rgba(59,130,246,0.15)] hc-surface--active dark:border-primary-400 dark:bg-primary-900/30 dark:shadow-[0_0_0_4px_rgba(56,189,248,0.25)]'
: 'border-neutral-200 hover:border-neutral-300 hover:bg-neutral-50 dark:border-neutral-700 dark:hover:border-neutral-500 dark:hover:bg-neutral-800/60'
} ${disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'}`}
aria-pressed={isSelected}
>
Expand All @@ -76,13 +76,15 @@ export const FormatPreviewCard = ({
<div
className={`rounded-lg p-2 ${
isSelected
? 'bg-primary-100 hc-chip hc-chip--fill'
: 'bg-neutral-100 hc-chip'
? 'bg-primary-100 hc-chip hc-chip--fill dark:bg-primary-900/50'
: 'bg-neutral-100 hc-chip dark:bg-neutral-800/70'
}`}
>
<svg
className={`h-5 w-5 ${
isSelected ? 'text-primary-600 hc-icon-accent' : 'text-neutral-600'
isSelected
? 'text-primary-600 hc-icon-accent dark:text-primary-200'
: 'text-neutral-600 dark:text-neutral-300'
}`}
fill="none"
viewBox="0 0 24 24"
Expand All @@ -93,9 +95,9 @@ export const FormatPreviewCard = ({
</svg>
</div>
<div>
<h3 className="text-base font-semibold text-neutral-900">{config.label}</h3>
<h3 className="text-base font-semibold text-neutral-900 dark:text-neutral-100">{config.label}</h3>
{isSelected && (
<span className="text-xs font-medium text-primary-600 hc-text-accent">
<span className="text-xs font-medium text-primary-600 hc-text-accent dark:text-primary-200">
Selected
</span>
)}
Expand All @@ -119,14 +121,14 @@ export const FormatPreviewCard = ({
</div>

{/* Description */}
<p className="mt-3 text-sm text-neutral-600">{config.description}</p>
<p className="mt-3 text-sm text-neutral-600 dark:text-neutral-300">{config.description}</p>

{/* Preview */}
<div className="mt-4 w-full rounded-lg border border-neutral-200 bg-white p-3 hc-surface-subtle">
<p className="mb-2 text-xs font-semibold uppercase tracking-wide text-neutral-500 hc-text-accent">
<div className="mt-4 w-full rounded-lg border border-neutral-200 bg-white p-3 hc-surface-subtle dark:border-neutral-700 dark:bg-neutral-900/40">
<p className="mb-2 text-xs font-semibold uppercase tracking-wide text-neutral-500 hc-text-accent dark:text-neutral-400">
Preview
</p>
<pre className="whitespace-pre-wrap font-sans text-xs text-neutral-700">
<pre className="whitespace-pre-wrap font-sans text-xs text-neutral-700 dark:text-neutral-200">
{config.preview}
</pre>
</div>
Expand Down
Loading