From 5fa638dc401f854130534f1c539fdf4ce17155df Mon Sep 17 00:00:00 2001 From: Mark Miro Date: Mon, 22 Sep 2025 06:00:25 -0700 Subject: [PATCH 01/35] WIP --- packages/schema/src/queries/cellOrdering.ts | 2 +- .../notebooks/notebook/NotebookControls.tsx | 186 ++++++++++++++++++ .../notebooks/notebook/NotebookHeader.tsx | 4 +- 3 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 src/components/notebooks/notebook/NotebookControls.tsx diff --git a/packages/schema/src/queries/cellOrdering.ts b/packages/schema/src/queries/cellOrdering.ts index ecaded31..012a0a48 100644 --- a/packages/schema/src/queries/cellOrdering.ts +++ b/packages/schema/src/queries/cellOrdering.ts @@ -10,7 +10,7 @@ import { queryDb } from "@runtimed/schema"; // Get all cells with their fractional indices, sorted export const cellsWithIndices$ = queryDb( tables.cells - .select("id", "fractionalIndex", "cellType") + .select("id", "fractionalIndex", "cellType", "executionCount") .orderBy("fractionalIndex", "asc"), { label: "cells.withIndices" } ); diff --git a/src/components/notebooks/notebook/NotebookControls.tsx b/src/components/notebooks/notebook/NotebookControls.tsx new file mode 100644 index 00000000..7b2e15e9 --- /dev/null +++ b/src/components/notebooks/notebook/NotebookControls.tsx @@ -0,0 +1,186 @@ +import { useAuthenticatedUser } from "@/auth/index.js"; +import { Button } from "@/components/ui/button"; +import { useConfirm } from "@/components/ui/confirm"; +import { Label } from "@/components/ui/label"; +import { Switch } from "@/components/ui/switch"; +import { useRuntimeHealth } from "@/hooks/useRuntimeHealth"; +import { useQuery, useStore } from "@livestore/react"; +import { events, queries, queryDb, tables } from "@runtimed/schema"; +import { Eraser, Play, Square, Trash, Undo2 } from "lucide-react"; +import { useCallback, useState } from "react"; +import { toast } from "sonner"; + +export function NotebookControls() { + const { confirm } = useConfirm(); + + const cellQueue = useQuery( + queryDb( + tables.cells + .select() + .where({ executionState: { op: "IN", value: ["running", "queued"] } }) + .orderBy("fractionalIndex", "asc") + ) + ); + + const { runAllCells } = useRunAllCells(); + const { deleteAllCells } = useDeleteAllCells(); + const { stopAllExecution } = useStopAllExecution(); + const { clearAllOutputs } = useClearAllOutputs(); + const { restartAndRunAllCells } = useRestartAndRunAllCells(); + const [showAICells, setShowAICells] = useState(false); + + return ( + <> + + + {cellQueue.length > 0 ? ( + + ) : ( + <> + + + + )} + + + ); +} + +function useDeleteAllCells() { + const { store } = useStore(); + const cells = useQuery(queries.cellsWithIndices$); + const userId = useAuthenticatedUser(); + + const deleteAllCells = useCallback(() => { + cells.forEach((cell) => { + store.commit(events.cellDeleted({ id: cell.id, actorId: userId })); + }); + }, [cells, store, userId]); + + return { deleteAllCells }; +} + +function useRunAllCells() { + const { store } = useStore(); + const cells = useQuery(queries.cellsWithIndices$); + const userId = useAuthenticatedUser(); + + const runAllCells = useCallback(() => { + cells + // We especially don't want to run AI cells + .filter((cell) => cell.cellType === "code") + .forEach((cell) => { + store.commit( + events.executionRequested({ + cellId: cell.id, + executionCount: (cell.executionCount || 0) + 1, + requestedBy: userId, + actorId: userId, + queueId: `exec-${Date.now()}-${Math.random().toString(36).slice(2)}`, + }) + ); + }); + }, [cells, store, userId]); + + return { runAllCells }; +} + +function useRestartAndRunAllCells() { + const { hasActiveRuntime } = useRuntimeHealth(); + + const restartAndRunAllCells = useCallback(() => { + if (hasActiveRuntime) { + toast.info("Restart your runtime manually and run all cells"); + } else { + toast.error("No active runtime found"); + } + }, [hasActiveRuntime]); + + return { restartAndRunAllCells }; +} + +function useStopAllExecution() { + const { store } = useStore(); + + const userId = useAuthenticatedUser(); + + const stopAllExecution = useCallback( + (cellIds: string[]) => { + for (const cellId of cellIds) { + store.commit( + events.executionCancelled({ + queueId: cellId, + cellId: cellId, + cancelledBy: userId, + reason: "User interrupted execution", + }) + ); + } + }, + [store, userId] + ); + + return { stopAllExecution }; +} + +function useClearAllOutputs() { + const { store } = useStore(); + const cells = useQuery(queries.cellsWithIndices$); + const userId = useAuthenticatedUser(); + + const clearAllOutputs = useCallback(() => { + cells.forEach((cell) => { + store.commit( + events.cellOutputsCleared({ + cellId: cell.id, + clearedBy: userId, + wait: false, + }) + ); + }); + }, [cells, store, userId]); + + return { clearAllOutputs }; +} diff --git a/src/components/notebooks/notebook/NotebookHeader.tsx b/src/components/notebooks/notebook/NotebookHeader.tsx index 699b9871..1d58d72a 100644 --- a/src/components/notebooks/notebook/NotebookHeader.tsx +++ b/src/components/notebooks/notebook/NotebookHeader.tsx @@ -6,6 +6,7 @@ import { Collaborator } from "../types.js"; import { Button } from "../../ui/button.js"; import { SimpleUserProfile } from "../SimpleUserProfile.js"; import type { NotebookProcessed } from "../types.js"; +import { NotebookControls } from "./NotebookControls.js"; import { TitleEditor } from "./TitleEditor.js"; export function NotebookHeader({ @@ -23,12 +24,13 @@ export function NotebookHeader({
-
+
+
{/* Right side - Mobile optimized */} From f04566e8a6dc7de8abfc9dbf5c6a440d1750cea3 Mon Sep 17 00:00:00 2001 From: Mark Miro Date: Mon, 22 Sep 2025 06:06:17 -0700 Subject: [PATCH 02/35] WIP first pass dropdown menu --- .../notebooks/notebook/NotebookControls.tsx | 113 +++++++++++------- .../notebooks/notebook/NotebookHeader.tsx | 6 +- 2 files changed, 70 insertions(+), 49 deletions(-) diff --git a/src/components/notebooks/notebook/NotebookControls.tsx b/src/components/notebooks/notebook/NotebookControls.tsx index 7b2e15e9..5101d53f 100644 --- a/src/components/notebooks/notebook/NotebookControls.tsx +++ b/src/components/notebooks/notebook/NotebookControls.tsx @@ -3,10 +3,24 @@ import { Button } from "@/components/ui/button"; import { useConfirm } from "@/components/ui/confirm"; import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { useRuntimeHealth } from "@/hooks/useRuntimeHealth"; import { useQuery, useStore } from "@livestore/react"; import { events, queries, queryDb, tables } from "@runtimed/schema"; -import { Eraser, Play, Square, Trash, Undo2 } from "lucide-react"; +import { + Eraser, + MoreHorizontal, + Play, + Square, + Trash, + Undo2, +} from "lucide-react"; import { useCallback, useState } from "react"; import { toast } from "sonner"; @@ -31,52 +45,59 @@ export function NotebookControls() { return ( <> - - - {cellQueue.length > 0 ? ( - - ) : ( - <> - - - - )} + + + {cellQueue.length > 0 ? ( + stopAllExecution(cellQueue.map((cell) => cell.id))} + > + + Stop All + + ) : ( + <> + + + Run All Cells + + + + + + + Restart and Run All Cells + + + )} + + + + Clear All Outputs + + + confirm({ + title: "Delete All Cells", + description: "Are you sure you want to delete all cells?", + onConfirm: deleteAllCells, + actionButtonText: "Delete All Cells", + }) + } + className="text-red-600 focus:text-red-600" + > + + Delete All Cells + + +
{/* Right side - Mobile optimized */} @@ -46,8 +45,8 @@ export function NotebookHeader({
{/* Metadata - Mobile optimized */} -
-
+
+
{/* Owner name - Mobile: Show on mobile with CollaboratorAvatars */}
@@ -69,6 +68,7 @@ export function NotebookHeader({ setIsSharingModalOpen={() => setIsSharingModalOpen(true)} />
+
From f1f6dabd4741c3e10cd98131b5d3b5325dd7f455 Mon Sep 17 00:00:00 2001 From: Mark Miro Date: Mon, 22 Sep 2025 06:13:35 -0700 Subject: [PATCH 03/35] Move hide AI cells button --- .../notebooks/notebook/NotebookControls.tsx | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/components/notebooks/notebook/NotebookControls.tsx b/src/components/notebooks/notebook/NotebookControls.tsx index 5101d53f..acbaa297 100644 --- a/src/components/notebooks/notebook/NotebookControls.tsx +++ b/src/components/notebooks/notebook/NotebookControls.tsx @@ -1,8 +1,6 @@ import { useAuthenticatedUser } from "@/auth/index.js"; import { Button } from "@/components/ui/button"; import { useConfirm } from "@/components/ui/confirm"; -import { Label } from "@/components/ui/label"; -import { Switch } from "@/components/ui/switch"; import { DropdownMenu, DropdownMenuContent, @@ -10,10 +8,12 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { Switch } from "@/components/ui/switch"; import { useRuntimeHealth } from "@/hooks/useRuntimeHealth"; import { useQuery, useStore } from "@livestore/react"; import { events, queries, queryDb, tables } from "@runtimed/schema"; import { + Bot, Eraser, MoreHorizontal, Play, @@ -43,8 +43,19 @@ export function NotebookControls() { const { restartAndRunAllCells } = useRestartAndRunAllCells(); const [showAICells, setShowAICells] = useState(false); + const handleStopAll = useCallback( + () => stopAllExecution(cellQueue.map((cell) => cell.id)), + [stopAllExecution, cellQueue] + ); + return ( - <> +
+ {cellQueue.length > 0 && ( + + )}
); } From 35e3eccfd7b132fea0e85d7a391f75d6108730e9 Mon Sep 17 00:00:00 2001 From: Mark Miro Date: Mon, 22 Sep 2025 06:17:38 -0700 Subject: [PATCH 04/35] Add toasts for stopping cells --- .../notebooks/notebook/NotebookControls.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/notebooks/notebook/NotebookControls.tsx b/src/components/notebooks/notebook/NotebookControls.tsx index acbaa297..845612fd 100644 --- a/src/components/notebooks/notebook/NotebookControls.tsx +++ b/src/components/notebooks/notebook/NotebookControls.tsx @@ -43,10 +43,14 @@ export function NotebookControls() { const { restartAndRunAllCells } = useRestartAndRunAllCells(); const [showAICells, setShowAICells] = useState(false); - const handleStopAll = useCallback( - () => stopAllExecution(cellQueue.map((cell) => cell.id)), - [stopAllExecution, cellQueue] - ); + const handleStopAll = useCallback(() => { + if (cellQueue.length === 0) { + toast.info("No cells to stop"); + return; + } + toast.info("Stopping all cells"); + stopAllExecution(cellQueue.map((cell) => cell.id)); + }, [stopAllExecution, cellQueue]); return (
From 1395edc764f78c84c5b97e5f755863cec092950e Mon Sep 17 00:00:00 2001 From: Mark Miro Date: Mon, 22 Sep 2025 06:44:33 -0700 Subject: [PATCH 05/35] Implement hide AI cells --- src/components/notebook/NotebookContent.tsx | 12 ++++- .../notebooks/notebook/NotebookControls.tsx | 9 ++-- .../notebooks/notebook/NotebookPage.tsx | 32 +++++++------ src/contexts/HideAiCellsContext.tsx | 48 +++++++++++++++++++ src/hooks/useHideAiCells.ts | 5 ++ 5 files changed, 86 insertions(+), 20 deletions(-) create mode 100644 src/contexts/HideAiCellsContext.tsx create mode 100644 src/hooks/useHideAiCells.ts diff --git a/src/components/notebook/NotebookContent.tsx b/src/components/notebook/NotebookContent.tsx index f4134f1b..16a0dd35 100644 --- a/src/components/notebook/NotebookContent.tsx +++ b/src/components/notebook/NotebookContent.tsx @@ -8,14 +8,24 @@ import { CellBetweener } from "./cell/CellBetweener.js"; import { EmptyStateCellAdder } from "./EmptyStateCellAdder"; import { contextSelectionMode$ } from "./signals/ai-context.js"; import { focusedCellSignal$, hasManuallyFocused$ } from "./signals/focus.js"; +import { useHideAiCells } from "../../hooks/useHideAiCells"; export const NotebookContent = () => { const { store } = useStore(); - const cellReferences = useQuery(queries.cellsWithIndices$); + const allCellReferences = useQuery(queries.cellsWithIndices$); + const { hideAiCells } = useHideAiCells(); const focusedCellId = useQuery(focusedCellSignal$); const hasManuallyFocused = useQuery(hasManuallyFocused$); + // Filter out AI cells if hideAiCells is true + const cellReferences = React.useMemo(() => { + if (!hideAiCells) { + return allCellReferences; + } + return allCellReferences.filter((cell) => cell.cellType !== "ai"); + }, [allCellReferences, hideAiCells]); + // Reset focus when focused cell changes or is removed React.useEffect(() => { if (focusedCellId && !cellReferences.find((c) => c.id === focusedCellId)) { diff --git a/src/components/notebooks/notebook/NotebookControls.tsx b/src/components/notebooks/notebook/NotebookControls.tsx index 845612fd..19f8f4c3 100644 --- a/src/components/notebooks/notebook/NotebookControls.tsx +++ b/src/components/notebooks/notebook/NotebookControls.tsx @@ -10,6 +10,7 @@ import { } from "@/components/ui/dropdown-menu"; import { Switch } from "@/components/ui/switch"; import { useRuntimeHealth } from "@/hooks/useRuntimeHealth"; +import { useHideAiCells } from "@/hooks/useHideAiCells"; import { useQuery, useStore } from "@livestore/react"; import { events, queries, queryDb, tables } from "@runtimed/schema"; import { @@ -21,7 +22,7 @@ import { Trash, Undo2, } from "lucide-react"; -import { useCallback, useState } from "react"; +import { useCallback } from "react"; import { toast } from "sonner"; export function NotebookControls() { @@ -41,7 +42,7 @@ export function NotebookControls() { const { stopAllExecution } = useStopAllExecution(); const { clearAllOutputs } = useClearAllOutputs(); const { restartAndRunAllCells } = useRestartAndRunAllCells(); - const [showAICells, setShowAICells] = useState(false); + const { hideAiCells, toggleHideAiCells } = useHideAiCells(); const handleStopAll = useCallback(() => { if (cellQueue.length === 0) { @@ -98,8 +99,8 @@ export function NotebookControls() { Hide AI Cells
setShowAICells(!showAICells)} + checked={hideAiCells} + onCheckedChange={toggleHideAiCells} /> diff --git a/src/components/notebooks/notebook/NotebookPage.tsx b/src/components/notebooks/notebook/NotebookPage.tsx index 570585a3..c065b2fb 100644 --- a/src/components/notebooks/notebook/NotebookPage.tsx +++ b/src/components/notebooks/notebook/NotebookPage.tsx @@ -8,6 +8,7 @@ import { LoadingState } from "../../loading/LoadingState.js"; import { NotebookContent } from "../../notebook/NotebookContent.js"; import { NotebookSidebar } from "../../notebook/NotebookSidebar.js"; +import { HideAiCellsProvider } from "../../../contexts/HideAiCellsContext"; import { useIsMobile } from "@/hooks/use-mobile.js"; import { ChatModeProvider } from "@/hooks/useChatMode.js"; @@ -86,27 +87,28 @@ function NotebookPageWithIdAndNotebook({ onUpdate={refetch} onAiPanelToggle={setIsAiPanelOpen} /> -
- setIsSharingModalOpen(true)} - /> - -
-
- -
+ + setIsSharingModalOpen(true)} + /> + +
+
+ +
+
-
+ {isScrolled && !isMobile && ( From 7bc676c44ac8d90a8c4db90db2108da297aa64c9 Mon Sep 17 00:00:00 2001 From: Mark Miro Date: Mon, 22 Sep 2025 08:50:57 -0700 Subject: [PATCH 07/35] Use standard function for generating a queue id --- src/components/notebook/cell/ExecutableCell.tsx | 6 ++---- src/components/notebooks/notebook/NotebookControls.tsx | 3 ++- src/util/queue-id.ts | 3 +++ 3 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 src/util/queue-id.ts diff --git a/src/components/notebook/cell/ExecutableCell.tsx b/src/components/notebook/cell/ExecutableCell.tsx index e33d177b..0317ba40 100644 --- a/src/components/notebook/cell/ExecutableCell.tsx +++ b/src/components/notebook/cell/ExecutableCell.tsx @@ -38,6 +38,7 @@ import { MaybeCellOutputs } from "@/components/outputs/MaybeCellOutputs.js"; import { useToolApprovals } from "@/hooks/useToolApprovals.js"; import { AiToolApprovalOutput } from "../../outputs/shared-with-iframe/AiToolApprovalOutput.js"; import { cn } from "@/lib/utils.js"; +import { generateQueueId } from "@/util/queue-id.js"; // Cell-specific styling configuration const getCellStyling = (cellType: "code" | "sql" | "ai") => { @@ -191,15 +192,12 @@ export const ExecutableCell: React.FC = ({ ); // Generate unique queue ID - const queueId = `exec-${Date.now()}-${Math.random() - .toString(36) - .slice(2)}`; const executionCount = (cell.executionCount || 0) + 1; // Add to execution queue - runtimes will pick this up store.commit( events.executionRequested({ - queueId, + queueId: generateQueueId(), cellId: cell.id, executionCount, requestedBy: userId, diff --git a/src/components/notebooks/notebook/NotebookControls.tsx b/src/components/notebooks/notebook/NotebookControls.tsx index c8a25406..6c250cba 100644 --- a/src/components/notebooks/notebook/NotebookControls.tsx +++ b/src/components/notebooks/notebook/NotebookControls.tsx @@ -24,6 +24,7 @@ import { } from "lucide-react"; import { useCallback } from "react"; import { toast } from "sonner"; +import { generateQueueId } from "@/util/queue-id"; export function NotebookControls() { const { confirm } = useConfirm(); @@ -164,7 +165,7 @@ function useRunAllCells() { executionCount: (cell.executionCount || 0) + 1, requestedBy: userId, actorId: userId, - queueId: `exec-${Date.now()}-${Math.random().toString(36).slice(2)}`, + queueId: generateQueueId(), }) ); }); diff --git a/src/util/queue-id.ts b/src/util/queue-id.ts new file mode 100644 index 00000000..33374d57 --- /dev/null +++ b/src/util/queue-id.ts @@ -0,0 +1,3 @@ +export function generateQueueId() { + return `exec-${Date.now()}-${Math.random().toString(36).slice(2)}`; +} From 1d2a61b7072a4de1bf26c08351a60febdaf61678 Mon Sep 17 00:00:00 2001 From: Mark Miro Date: Mon, 22 Sep 2025 09:02:17 -0700 Subject: [PATCH 08/35] Fix stop all cells --- .../notebooks/notebook/NotebookControls.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/components/notebooks/notebook/NotebookControls.tsx b/src/components/notebooks/notebook/NotebookControls.tsx index 6c250cba..c42715e6 100644 --- a/src/components/notebooks/notebook/NotebookControls.tsx +++ b/src/components/notebooks/notebook/NotebookControls.tsx @@ -190,21 +190,27 @@ function useRestartAndRunAllCells() { function useStopAllExecution() { const { store } = useStore(); - const userId = useAuthenticatedUser(); const stopAllExecution = useCallback( (cellIds: string[]) => { - for (const cellId of cellIds) { + const queueEntries = store.query( + queryDb( + tables.executionQueue + .select() + .where({ cellId: { op: "IN", value: cellIds } }) + ) + ); + queueEntries.forEach((queueEntry) => { store.commit( events.executionCancelled({ - queueId: cellId, - cellId: cellId, + queueId: queueEntry.id, + cellId: queueEntry.cellId, cancelledBy: userId, reason: "User interrupted execution", }) ); - } + }); }, [store, userId] ); From db46fd829061b97adb860fe76bbbcb3b38ab92f8 Mon Sep 17 00:00:00 2001 From: Mark Miro Date: Mon, 22 Sep 2025 19:44:39 -0700 Subject: [PATCH 09/35] Do filtering on sqlite side --- packages/schema/src/queries/cellOrdering.ts | 17 +++++++++++++++++ src/components/notebook/NotebookContent.tsx | 12 +++--------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/schema/src/queries/cellOrdering.ts b/packages/schema/src/queries/cellOrdering.ts index 012a0a48..bbc52909 100644 --- a/packages/schema/src/queries/cellOrdering.ts +++ b/packages/schema/src/queries/cellOrdering.ts @@ -15,6 +15,23 @@ export const cellsWithIndices$ = queryDb( { label: "cells.withIndices" } ); +// TODO: if we want to keep the AI filter, we should update all call sites of `cellsWithIndices$` with this one +export const cellsWithIndices2$ = ({ + filterOutAiCells, +}: { + filterOutAiCells: boolean; +}) => + queryDb( + tables.cells + .select("id", "fractionalIndex", "cellType") + .where(filterOutAiCells ? { cellType: { op: "!=", value: "ai" } } : {}) + .orderBy("fractionalIndex", "asc"), + { + label: `cells.withIndices.${filterOutAiCells ? "noAi" : "all"}`, + deps: [filterOutAiCells.toString()], + } + ); + // Get just the cell ordering information (minimal fields) export const cellOrdering$ = queryDb( tables.cells diff --git a/src/components/notebook/NotebookContent.tsx b/src/components/notebook/NotebookContent.tsx index 16a0dd35..0f71b16e 100644 --- a/src/components/notebook/NotebookContent.tsx +++ b/src/components/notebook/NotebookContent.tsx @@ -12,20 +12,14 @@ import { useHideAiCells } from "../../hooks/useHideAiCells"; export const NotebookContent = () => { const { store } = useStore(); - const allCellReferences = useQuery(queries.cellsWithIndices$); const { hideAiCells } = useHideAiCells(); + const cellReferences = useQuery( + queries.cellsWithIndices2$({ filterOutAiCells: hideAiCells }) + ); const focusedCellId = useQuery(focusedCellSignal$); const hasManuallyFocused = useQuery(hasManuallyFocused$); - // Filter out AI cells if hideAiCells is true - const cellReferences = React.useMemo(() => { - if (!hideAiCells) { - return allCellReferences; - } - return allCellReferences.filter((cell) => cell.cellType !== "ai"); - }, [allCellReferences, hideAiCells]); - // Reset focus when focused cell changes or is removed React.useEffect(() => { if (focusedCellId && !cellReferences.find((c) => c.id === focusedCellId)) { From d1c6ccd78f401ffbee8660217322d2396fa042d2 Mon Sep 17 00:00:00 2001 From: Mark Miro Date: Mon, 22 Sep 2025 19:47:39 -0700 Subject: [PATCH 10/35] Show message if user adds AI cell if AI cells are hidden --- src/components/notebook/EmptyStateCellAdder.tsx | 12 +++++++++++- src/components/notebook/cell/CellAdder.tsx | 11 ++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/components/notebook/EmptyStateCellAdder.tsx b/src/components/notebook/EmptyStateCellAdder.tsx index fb138d64..c1a536a3 100644 --- a/src/components/notebook/EmptyStateCellAdder.tsx +++ b/src/components/notebook/EmptyStateCellAdder.tsx @@ -7,9 +7,13 @@ import { SqlCellButton, AiCellButton, } from "./cell/CellTypeButtons"; +import { useHideAiCells } from "@/hooks/useHideAiCells"; +import { toast } from "sonner"; export const EmptyStateCellAdder: React.FC = () => { const { addCell } = useAddCell(); + const { hideAiCells } = useHideAiCells(); + return (
@@ -41,7 +45,13 @@ export const EmptyStateCellAdder: React.FC = () => { addCell(undefined, "ai")} + onClick={() => { + if (hideAiCells) { + toast.success("AI cells are hidden"); + } else { + addCell(undefined, "ai"); + } + }} className="flex items-center gap-2" />
diff --git a/src/components/notebook/cell/CellAdder.tsx b/src/components/notebook/cell/CellAdder.tsx index f4b26017..3b9f0d27 100644 --- a/src/components/notebook/cell/CellAdder.tsx +++ b/src/components/notebook/cell/CellAdder.tsx @@ -6,6 +6,8 @@ import { SqlCellButton, AiCellButton, } from "./CellTypeButtons"; +import { toast } from "sonner"; +import { useHideAiCells } from "@/hooks/useHideAiCells"; export function CellAdder({ position, @@ -15,6 +17,7 @@ export function CellAdder({ className?: string; }) { const { addCell } = useAddCell(); + const { hideAiCells } = useHideAiCells(); return (
addCell(undefined, "ai", position)} + onClick={() => { + if (hideAiCells) { + toast.success("AI cells are hidden"); + } else { + addCell(undefined, "ai", position); + } + }} />
); From 66201068a492148e76bd2d8e6a19ee7f18597e12 Mon Sep 17 00:00:00 2001 From: Mark Miro Date: Mon, 22 Sep 2025 19:49:42 -0700 Subject: [PATCH 11/35] Add warning if hiding AI cells --- src/components/notebooks/notebook/NotebookControls.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/notebooks/notebook/NotebookControls.tsx b/src/components/notebooks/notebook/NotebookControls.tsx index c42715e6..72178ac1 100644 --- a/src/components/notebooks/notebook/NotebookControls.tsx +++ b/src/components/notebooks/notebook/NotebookControls.tsx @@ -101,8 +101,11 @@ export function NotebookControls() { e.preventDefault()}>