Skip to content

Commit 30286a4

Browse files
authored
Feature/cell visibility toggles (#44)
* Add cell visibility toggle schema fields and events - Add sourceVisible and outputVisible boolean fields to cells table - Add cellSourceVisibilityToggled and cellOutputVisibilityToggled events - Add corresponding materializers for visibility state updates - Default both visibility fields to true for backward compatibility * Implement visibility toggles for code and markdown cells - Add Eye and EyeOff icons to cell controls on hover - Add toggle handlers for source and output visibility - Wrap source textarea in sourceVisible conditional - Wrap output area in outputVisible conditional - Add visual separator between visibility toggles and other controls - Use muted styling when content is hidden * Implement visibility toggles for SQL cells - Add visibility toggle handlers and buttons to SQL cell controls - Wrap query textarea in sourceVisible conditional - Wrap query results in outputVisible conditional - Output toggle only shows when results are available - Maintain consistent styling with other cell types * Implement visibility toggles for AI cells - Add visibility toggle handlers and buttons to AI cell controls - Wrap prompt textarea in sourceVisible conditional - Wrap AI responses in outputVisible conditional - Output toggle only shows when responses are available - Consistent behavior across all cell types * Fix kernel status display inconsistency - Replace hasActiveKernel with activeKernel in status text logic - Add proper handling for stale kernel connections - Update color indicators to include stale state with amber color - Resolve inconsistency between main status and detailed panel - Stale kernels now show 'Connected (Stale)' instead of 'Disconnected' * Move output visibility toggle to execution summary bar - Remove output toggle from cell controls for all cell types - Add output toggle to right side of execution summary bar - Position toggle next to execution time for better context - Use smaller button size (h-5 w-5) to fit in summary bar - Only show toggle when output exists to hide - Improves UX by placing control near the content it affects * Replace eye icons with chevron icons for visibility toggles - Replace Eye/EyeOff icons with ChevronUp/ChevronDown for less creepy UI - Add hover/focus visibility to output toggles (fade when not active) - Update tooltip text to be more descriptive (Hide/Show instead of Toggle) - ChevronUp = visible content, ChevronDown = hidden content - Output toggle only appears on hover or when cell is focused * Use arrow icons for cell movement to avoid chevron conflict - Replace ChevronUp/ChevronDown with ArrowUp/ArrowDown for move controls - Resolves icon conflict with visibility toggles using chevrons - ArrowUp/ArrowDown more clearly indicate directional movement - ChevronUp/ChevronDown reserved for show/hide content actions * Adjust play button position when source is collapsed - Move play button up when source is hidden to align with cell header - Conditional positioning: 0.375rem when visible, -1.5rem when hidden - Prevents disconnected play button appearance in collapsed state - Applied to all cell types: Code, SQL, and AI cells * Update HANDOFF.md to reflect Task 4 completion - Mark Source/Output Display Toggles as completed - Document implemented features and modified files - Add branch reference for review - Update current session status
1 parent 8984a95 commit 30286a4

File tree

6 files changed

+306
-122
lines changed

6 files changed

+306
-122
lines changed

HANDOFF.md

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
**Focus**: Async Python execution, AI context integration, and visibility controls
66

77
**Documentation fixes completed**: Updated all file references (mod-reactive.ts → kernel-adapter.ts, OPENAI_INTEGRATION.md → ai-features.md, etc.)
8+
**Task 4 completed**: Source/Output Display Toggles implemented and ready for review
89

910
### Task 1: Switch to Async Python Code Execution ⏳
1011
**Goal**: Make Python code execution truly asynchronous in the kernel
@@ -42,21 +43,27 @@
4243
- `packages/web-client/src/components/notebook/AiCell.tsx` - Add visibility indicators
4344
- `packages/dev-server-kernel-ls-client/src/kernel-adapter.ts` - Filter cells in `gatherNotebookContext()` based on visibility settings
4445

45-
### Task 4: Source/Output Display Toggles 📱
46+
### Task 4: Source/Output Display Toggles ✅ COMPLETED
4647
**Goal**: Allow users to collapse/expand source code and outputs
4748
**Solution**: Add UI toggles for cell content visibility
4849

49-
**Features needed**:
50-
- Toggle to collapse/expand source code display
51-
- Toggle to collapse/expand output display
52-
- Preserve toggle state per user (using uiState table)
53-
- Keyboard shortcuts for quick toggling
54-
55-
**Files to modify**:
56-
- `shared/schema.ts` - Add collapsed state fields to uiState schema per cell
57-
- `packages/web-client/src/components/notebook/CodeCell.tsx` - Add collapse/expand buttons for source
58-
- `packages/web-client/src/components/notebook/RichOutput.tsx` - Add collapse/expand buttons for outputs
59-
- CSS classes for smooth expand/collapse animations with max-height transitions
50+
**Completed features**:
51+
- ✅ Toggle to collapse/expand source code display (chevron icons in cell controls)
52+
- ✅ Toggle to collapse/expand output display (chevron icons in execution summary bar)
53+
- ✅ Persistent state via LiveStore events (`sourceVisible`/`outputVisible` fields)
54+
- ✅ Real-time synchronization across collaborative sessions
55+
- ✅ Consistent implementation across Code, SQL, and AI cell types
56+
- ✅ Smart play button repositioning when source is collapsed
57+
- ✅ Hover-based visibility for reduced UI clutter
58+
59+
**Files modified**:
60+
-`shared/schema.ts` - Added `sourceVisible`/`outputVisible` fields with events and materializers
61+
-`packages/web-client/src/components/notebook/Cell.tsx` - Source/output visibility toggles
62+
-`packages/web-client/src/components/notebook/SqlCell.tsx` - SQL-specific toggles
63+
-`packages/web-client/src/components/notebook/AiCell.tsx` - AI-specific toggles
64+
- ✅ Icon improvements: ChevronUp/Down for show/hide, ArrowUp/Down for cell movement
65+
66+
**Branch**: `feature/cell-visibility-toggles` (ready for review)
6067

6168
## Current Work State
6269

packages/web-client/src/components/notebook/AiCell.tsx

Lines changed: 76 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
DropdownMenuTrigger
1414
} from '@/components/ui/dropdown-menu'
1515
import { RichOutput } from './RichOutput.js'
16-
import { Play, ChevronUp, ChevronDown, Plus, X, Bot, Code, FileText, Database } from 'lucide-react'
16+
import { Play, ChevronUp, ChevronDown, Plus, X, Bot, Code, FileText, Database, ArrowUp, ArrowDown } from 'lucide-react'
1717

1818
interface AiCellProps {
1919
cell: typeof tables.cells.Type
@@ -191,6 +191,20 @@ export const AiCell: React.FC<AiCellProps> = ({
191191
}))
192192
}, [cell.id, store])
193193

194+
const toggleSourceVisibility = useCallback(() => {
195+
store.commit(events.cellSourceVisibilityToggled({
196+
id: cell.id,
197+
sourceVisible: !cell.sourceVisible,
198+
}))
199+
}, [cell.id, cell.sourceVisible, store])
200+
201+
const toggleOutputVisibility = useCallback(() => {
202+
store.commit(events.cellOutputVisibilityToggled({
203+
id: cell.id,
204+
outputVisible: !cell.outputVisible,
205+
}))
206+
}, [cell.id, cell.outputVisible, store])
207+
194208
const changeProvider = useCallback((newProvider: string, newModel: string) => {
195209
store.commit(events.aiSettingsChanged({
196210
cellId: cell.id,
@@ -319,14 +333,27 @@ export const AiCell: React.FC<AiCellProps> = ({
319333

320334
{/* Cell Controls - visible on hover */}
321335
<div className="flex items-center gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
336+
{/* Visibility Toggles */}
337+
<Button
338+
variant="ghost"
339+
size="sm"
340+
onClick={toggleSourceVisibility}
341+
className={`h-7 w-7 p-0 hover:bg-muted/80 ${cell.sourceVisible ? '' : 'text-muted-foreground/60'}`}
342+
title={cell.sourceVisible ? 'Hide source' : 'Show source'}
343+
>
344+
{cell.sourceVisible ? <ChevronUp className="h-3 w-3" /> : <ChevronDown className="h-3 w-3" />}
345+
</Button>
346+
347+
{/* Separator */}
348+
<div className="w-px h-4 bg-border/50 mx-1" />
322349
<Button
323350
variant="ghost"
324351
size="sm"
325352
onClick={onMoveUp}
326353
className="h-7 w-7 p-0 hover:bg-muted/80"
327354
title="Move cell up"
328355
>
329-
<ChevronUp className="h-3 w-3" />
356+
<ArrowUp className="h-3 w-3" />
330357
</Button>
331358
<Button
332359
variant="ghost"
@@ -335,7 +362,7 @@ export const AiCell: React.FC<AiCellProps> = ({
335362
className="h-7 w-7 p-0 hover:bg-muted/80"
336363
title="Move cell down"
337364
>
338-
<ChevronDown className="h-3 w-3" />
365+
<ArrowDown className="h-3 w-3" />
339366
</Button>
340367
<Button
341368
variant="ghost"
@@ -361,7 +388,7 @@ export const AiCell: React.FC<AiCellProps> = ({
361388
{/* Cell Content with Left Gutter Play Button */}
362389
<div className="relative">
363390
{/* Play Button Breaking Through Left Border */}
364-
<div className="absolute -left-3 z-10" style={{ top: '0.375rem' }}>
391+
<div className="absolute -left-3 z-10" style={{ top: cell.sourceVisible ? '0.375rem' : '-1.5rem' }}>
365392
<Button
366393
variant="ghost"
367394
size="sm"
@@ -384,43 +411,62 @@ export const AiCell: React.FC<AiCellProps> = ({
384411
</div>
385412

386413
{/* Text Content Area */}
387-
<div className={`transition-colors py-1 pl-4 pr-4 ${
388-
autoFocus
389-
? 'bg-white'
390-
: 'bg-white'
391-
}`}>
392-
<div className="min-h-[1.5rem]">
393-
<Textarea
394-
ref={textareaRef}
395-
value={localSource}
396-
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setLocalSource(e.target.value)}
397-
onBlur={updateSource}
398-
onKeyDown={handleKeyDown}
399-
placeholder="Ask me anything about your notebook, data, or analysis..."
400-
className="min-h-[1.5rem] resize-none border-0 px-2 py-1 focus-visible:ring-0 font-mono bg-white w-full placeholder:text-muted-foreground/60 shadow-none"
401-
onFocus={handleFocus}
402-
/>
414+
{cell.sourceVisible && (
415+
<div className={`transition-colors py-1 pl-4 pr-4 ${
416+
autoFocus
417+
? 'bg-white'
418+
: 'bg-white'
419+
}`}>
420+
<div className="min-h-[1.5rem]">
421+
<Textarea
422+
ref={textareaRef}
423+
value={localSource}
424+
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setLocalSource(e.target.value)}
425+
onBlur={updateSource}
426+
onKeyDown={handleKeyDown}
427+
placeholder="Ask me anything about your notebook, data, or analysis..."
428+
className="min-h-[1.5rem] resize-none border-0 px-2 py-1 focus-visible:ring-0 font-mono bg-white w-full placeholder:text-muted-foreground/60 shadow-none"
429+
onFocus={handleFocus}
430+
/>
431+
</div>
403432
</div>
404-
</div>
433+
)}
405434
</div>
406435

407436
{/* Execution Summary - appears after input */}
408437
{(cell.executionCount || cell.executionState === 'running' || cell.executionState === 'queued') && (
409438
<div className="mt-1 pl-6 pr-4">
410-
<div className="text-xs text-muted-foreground pb-1">
411-
{cell.executionState === 'running' ? (
412-
'Generating...'
413-
) : cell.executionState === 'queued' ? (
414-
'Queued'
415-
) : cell.executionCount ? (
416-
'1.2s' /* TODO: Gather execution time */
417-
) : null}
439+
<div className="flex items-center justify-between text-xs text-muted-foreground pb-1">
440+
<span>
441+
{cell.executionState === 'running' ? (
442+
'Generating...'
443+
) : cell.executionState === 'queued' ? (
444+
'Queued'
445+
) : cell.executionCount ? (
446+
'1.2s' /* TODO: Gather execution time */
447+
) : null}
448+
</span>
449+
{outputs.length > 0 && (
450+
<Button
451+
variant="ghost"
452+
size="sm"
453+
onClick={toggleOutputVisibility}
454+
className={`h-5 w-5 p-0 hover:bg-muted/80 transition-opacity ${
455+
autoFocus
456+
? 'opacity-100'
457+
: 'opacity-0 group-hover:opacity-100'
458+
} ${cell.outputVisible ? '' : 'text-muted-foreground/60'}`}
459+
title={cell.outputVisible ? 'Hide output' : 'Show output'}
460+
>
461+
{cell.outputVisible ? <ChevronUp className="h-3 w-3" /> : <ChevronDown className="h-3 w-3" />}
462+
</Button>
463+
)}
418464
</div>
419465
</div>
420466
)}
421467

422468
{/* Output Area for AI Responses */}
423-
{outputs.length > 0 && (
469+
{outputs.length > 0 && cell.outputVisible && (
424470
<div className="mt-1 pl-6 pr-4 bg-background">
425471
{outputs
426472
.sort((a: OutputData, b: OutputData) => a.position - b.position)

0 commit comments

Comments
 (0)