Skip to content
Open
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
17 changes: 16 additions & 1 deletion src/components/notebook/NotebookContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,25 @@ import { contextSelectionMode$ } from "./signals/ai-context.js";
import { focusedCellSignal$, hasManuallyFocused$ } from "./signals/focus.js";
import { GripVerticalIcon } from "lucide-react";
import { useDragDropCellSort } from "@/hooks/useDragDropCellSort";
import { useCellFilter } from "@/contexts/CellFilterContext.js";

export const NotebookContent = () => {
const { store } = useStore();
const cells = useQuery(queries.cells$);
const allCells = useQuery(queries.cells$);
const { filters } = useCellFilter();

// Filter cells based on context values
const cells = React.useMemo(() => {
return allCells.filter((cell) => {
// if (cell.cellType === "code" && !filters.showCodeCells) {
// return false;
// }
if (cell.cellType === "ai" && !filters.showAiCells) {
return false;
}
return true;
});
}, [allCells, filters]);

const focusedCellId = useQuery(focusedCellSignal$);
const hasManuallyFocused = useQuery(hasManuallyFocused$);
Expand Down
120 changes: 63 additions & 57 deletions src/components/notebook/cell/ExecutableCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import { useFeatureFlag } from "@/contexts/FeatureFlagContext.js";
import { findBestAiModelForCell } from "./toolbars/ai-model-utils.js";
import { useAvailableAiModels } from "@/util/ai-models.js";
import { toast } from "sonner";
import { useCellFilter } from "@/contexts/CellFilterContext.js";

// Cell-specific styling configuration
const getCellStyling = (cellType: "code" | "sql" | "ai") => {
Expand Down Expand Up @@ -102,6 +103,10 @@ export const ExecutableCell: React.FC<ExecutableCellProps> = ({
const hasRunRef = useRef(false);
const cellRef = useRef<HTMLDivElement>(null);

const {
filters: { showCodeCells },
} = useCellFilter();

const {
registerEditor,
unregisterEditor,
Expand Down Expand Up @@ -477,7 +482,7 @@ export const ExecutableCell: React.FC<ExecutableCellProps> = ({
focusBorderColor={focusBorderColor}
>
{/* Cell Header */}
{!isSourceLessAiOutput && (
{!isSourceLessAiOutput && showCodeCells && (
<CellHeader
cellId={cell.id}
leftContent={
Expand Down Expand Up @@ -565,7 +570,7 @@ export const ExecutableCell: React.FC<ExecutableCellProps> = ({
)}

{/* Cell Content with Left Gutter Play Button - Desktop Only */}
{!isSourceLessAiOutput && (
{!isSourceLessAiOutput && showCodeCells && (
<div className="relative">
{/* Play Button Breaking Through Left Border - Desktop Only */}
<div
Expand Down Expand Up @@ -594,7 +599,7 @@ export const ExecutableCell: React.FC<ExecutableCellProps> = ({
)}

{/* Editor Content Area */}
{cell.sourceVisible && (
{cell.sourceVisible && showCodeCells && (
<div className="cell-content max-w-full overflow-x-auto bg-white py-1 pl-4 transition-colors">
<ErrorBoundary fallback={<div>Error rendering editor</div>}>
<Editor
Expand Down Expand Up @@ -624,63 +629,64 @@ export const ExecutableCell: React.FC<ExecutableCellProps> = ({
{/* Execution Summary - appears after input */}
{(cell.executionCount ||
cell.executionState === "running" ||
cell.executionState === "queued") && (
<div className="cell-content flex h-7 items-center justify-stretch pr-1 pl-6 sm:pr-4">
<div
className={cn(
"text-muted-foreground flex w-full items-center justify-between text-xs"
)}
>
<span
key={cell.executionCount}
className="animate-in fade-in duration-300 ease-in-out"
cell.executionState === "queued") &&
showCodeCells && (
<div className="cell-content flex h-7 items-center justify-stretch pr-1 pl-6 sm:pr-4">
<div
className={cn(
"text-muted-foreground flex w-full items-center justify-between text-xs"
)}
>
{cell.executionState === "running"
? "Executing..."
: cell.executionState === "queued"
? // Show count in case runtime is not responsive, to show that at least something is happening
`Queued for execution (execution count: ${cell.executionCount})`
: cell.executionCount
? cell.lastExecutionDurationMs
? `Executed in ${
cell.lastExecutionDurationMs < 1000
? `${cell.lastExecutionDurationMs}ms`
: `${(cell.lastExecutionDurationMs / 1000).toFixed(1)}s`
}`
: "Executed"
: null}
</span>
{(outputs.length > 0 || cell.executionState === "running") && (
<div className="flex items-center gap-2">
{!cell.outputVisible && hasOutputs && (
<span className="text-muted-foreground text-xs">
{outputs.length === 1
? "1 result hidden"
: `${outputs.length} results hidden`}
</span>
)}
<Button
variant="ghost"
size="sm"
onClick={toggleOutputVisibility}
className={`hover:bg-muted/80 h-6 w-6 p-0 transition-opacity sm:h-5 sm:w-5 ${
autoFocus
? "opacity-100"
: "opacity-100 sm:opacity-0 sm:group-hover:opacity-100"
} ${cell.outputVisible ? "" : "text-muted-foreground/60"}`}
title={cell.outputVisible ? "Hide results" : "Show results"}
>
{cell.outputVisible ? (
<ChevronUp className="h-4 w-4 sm:h-3 sm:w-3" />
) : (
<ChevronDown className="h-4 w-4 sm:h-3 sm:w-3" />
<span
key={cell.executionCount}
className="animate-in fade-in duration-300 ease-in-out"
>
{cell.executionState === "running"
? "Executing..."
: cell.executionState === "queued"
? // Show count in case runtime is not responsive, to show that at least something is happening
`Queued for execution (execution count: ${cell.executionCount})`
: cell.executionCount
? cell.lastExecutionDurationMs
? `Executed in ${
cell.lastExecutionDurationMs < 1000
? `${cell.lastExecutionDurationMs}ms`
: `${(cell.lastExecutionDurationMs / 1000).toFixed(1)}s`
}`
: "Executed"
: null}
</span>
{(outputs.length > 0 || cell.executionState === "running") && (
<div className="flex items-center gap-2">
{!cell.outputVisible && hasOutputs && (
<span className="text-muted-foreground text-xs">
{outputs.length === 1
? "1 result hidden"
: `${outputs.length} results hidden`}
</span>
)}
</Button>
</div>
)}
<Button
variant="ghost"
size="sm"
onClick={toggleOutputVisibility}
className={`hover:bg-muted/80 h-6 w-6 p-0 transition-opacity sm:h-5 sm:w-5 ${
autoFocus
? "opacity-100"
: "opacity-100 sm:opacity-0 sm:group-hover:opacity-100"
} ${cell.outputVisible ? "" : "text-muted-foreground/60"}`}
title={cell.outputVisible ? "Hide results" : "Show results"}
>
{cell.outputVisible ? (
<ChevronUp className="h-4 w-4 sm:h-3 sm:w-3" />
) : (
<ChevronDown className="h-4 w-4 sm:h-3 sm:w-3" />
)}
</Button>
</div>
)}
</div>
</div>
</div>
)}
)}

{/* Output Area */}

Expand Down
59 changes: 49 additions & 10 deletions src/components/notebooks/notebook/NotebookControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ import { Button } from "@/components/ui/button";
import { useConfirm } from "@/components/ui/confirm";
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
Expand All @@ -21,6 +24,7 @@ import type { NotebookProcessed } from "../types";
import { useAuthenticatedUser } from "@/auth/index.js";
import { useDebug } from "@/components/debug/debug-mode";
import { Spinner } from "@/components/ui/Spinner";
import { useCellFilter } from "@/contexts/CellFilterContext.js";
import { useRuntimeHealth } from "@/hooks/useRuntimeHealth";
import { runnableCellsWithIndices$, runningCells$ } from "@/queries";
import { generateQueueId } from "@/util/queue-id";
Expand Down Expand Up @@ -80,18 +84,32 @@ export function NotebookControls({
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{allowBulkNotebookControls && (
<BulkNotebookActions
cellQueue={cellQueue}
onCancelAll={handleCancelAll}
onClearAllOutputs={handleClearAllOutputs}
/>
<DropdownMenuGroup>
<DropdownMenuLabel>Execution</DropdownMenuLabel>
<BulkNotebookActions
cellQueue={cellQueue}
onCancelAll={handleCancelAll}
onClearAllOutputs={handleClearAllOutputs}
/>
</DropdownMenuGroup>
)}
<CreateNotebookAction />
<DuplicateAction notebook={notebook} />
{exportEnabled && <ExportAction />}
<DropdownMenuGroup>
<DropdownMenuLabel>View</DropdownMenuLabel>
<CellFilterActions />
</DropdownMenuGroup>
<DropdownMenuSeparator />
{debug.enabled && <DeleteAllCellsAction />}
<DeleteAction notebook={notebook} />
<DropdownMenuGroup>
{/* <DropdownMenuLabel>Notebook</DropdownMenuLabel> */}
<CreateNotebookAction />
<DuplicateAction notebook={notebook} />
{exportEnabled && <ExportAction />}
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
{/* <DropdownMenuLabel>Danger Zone</DropdownMenuLabel> */}
{debug.enabled && <DeleteAllCellsAction />}
<DeleteAction notebook={notebook} />
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
Expand Down Expand Up @@ -350,3 +368,24 @@ function useRestartAndRunAllCells() {

return { restartAndRunAllCells };
}

function CellFilterActions() {
const { filters, setShowCodeCells, setShowAiCells } = useCellFilter();

return (
<>
<DropdownMenuCheckboxItem
checked={filters.showCodeCells}
onCheckedChange={setShowCodeCells}
>
Show Code Cells
</DropdownMenuCheckboxItem>
<DropdownMenuCheckboxItem
checked={filters.showAiCells}
onCheckedChange={setShowAiCells}
>
Show AI Cells
</DropdownMenuCheckboxItem>
</>
);
}
13 changes: 8 additions & 5 deletions src/components/notebooks/notebook/NotebookPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { useNavigateToCanonicalUrl, useNotebook } from "./helpers.js";
import { NotebookHeader } from "./NotebookHeader.js";
import { AvailableAiModelsProvider } from "@/util/ai-models.js";
import { SidebarItemProvider } from "@/contexts/SidebarItemContext.js";
import { CellFilterProvider } from "@/contexts/CellFilterContext.js";

export const NotebookPage: React.FC = () => {
const { id } = useParams<{ id: string }>();
Expand All @@ -31,11 +32,13 @@ export const NotebookPage: React.FC = () => {

return (
<CustomLiveStoreProvider storeId={id}>
<ChatModeProvider>
<SidebarItemProvider>
<NotebookPageWithId id={id} />
</SidebarItemProvider>
</ChatModeProvider>
<CellFilterProvider>
<ChatModeProvider>
<SidebarItemProvider>
<NotebookPageWithId id={id} />
</SidebarItemProvider>
</ChatModeProvider>
</CellFilterProvider>
</CustomLiveStoreProvider>
);
};
Expand Down
Loading