diff --git a/apps/desktop/src/main/notebook-storage.ts b/apps/desktop/src/main/notebook-storage.ts index e29abdb..c37396e 100644 --- a/apps/desktop/src/main/notebook-storage.ts +++ b/apps/desktop/src/main/notebook-storage.ts @@ -46,13 +46,22 @@ function rowToNotebook(row: NotebookRow): Notebook { } } +function parsePinnedResult(raw: string): PinnedResult | null { + try { + return JSON.parse(raw) as PinnedResult + } catch { + log.warn('Corrupt pinned_result JSON, falling back to null') + return null + } +} + function rowToCell(row: CellRow): NotebookCell { return { id: row.id, notebookId: row.notebook_id, type: row.type, content: row.content, - pinnedResult: row.pinned_result ? (JSON.parse(row.pinned_result) as PinnedResult) : null, + pinnedResult: row.pinned_result ? parsePinnedResult(row.pinned_result) : null, order: row.order_index, createdAt: row.created_at, updatedAt: row.updated_at @@ -111,16 +120,14 @@ export class NotebookStorage { } getNotebook(id: string): NotebookWithCells | null { - const notebookRow = this.db - .prepare('SELECT * FROM notebooks WHERE id = ?') - .get(id) as NotebookRow | undefined + const notebookRow = this.db.prepare('SELECT * FROM notebooks WHERE id = ?').get(id) as + | NotebookRow + | undefined if (!notebookRow) return null const cellRows = this.db - .prepare( - 'SELECT * FROM notebook_cells WHERE notebook_id = ? ORDER BY order_index ASC' - ) + .prepare('SELECT * FROM notebook_cells WHERE notebook_id = ? ORDER BY order_index ASC') .all(id) as CellRow[] return { @@ -144,9 +151,9 @@ export class NotebookStorage { } updateNotebook(id: string, input: UpdateNotebookInput): Notebook | null { - const existing = this.db - .prepare('SELECT * FROM notebooks WHERE id = ?') - .get(id) as NotebookRow | undefined + const existing = this.db.prepare('SELECT * FROM notebooks WHERE id = ?').get(id) as + | NotebookRow + | undefined if (!existing) return null const now = Date.now() @@ -154,9 +161,7 @@ export class NotebookStorage { const folder = input.folder !== undefined ? input.folder : existing.folder this.db - .prepare( - 'UPDATE notebooks SET title = ?, folder = ?, updated_at = ? WHERE id = ?' - ) + .prepare('UPDATE notebooks SET title = ?, folder = ?, updated_at = ? WHERE id = ?') .run(title, folder, now, id) return rowToNotebook( @@ -227,9 +232,9 @@ export class NotebookStorage { } updateCell(id: string, input: UpdateCellInput): NotebookCell | null { - const existing = this.db - .prepare('SELECT * FROM notebook_cells WHERE id = ?') - .get(id) as CellRow | undefined + const existing = this.db.prepare('SELECT * FROM notebook_cells WHERE id = ?').get(id) as + | CellRow + | undefined if (!existing) return null const now = Date.now() @@ -254,9 +259,9 @@ export class NotebookStorage { } deleteCell(id: string): void { - const cell = this.db - .prepare('SELECT * FROM notebook_cells WHERE id = ?') - .get(id) as CellRow | undefined + const cell = this.db.prepare('SELECT * FROM notebook_cells WHERE id = ?').get(id) as + | CellRow + | undefined if (!cell) return this.db.prepare('DELETE FROM notebook_cells WHERE id = ?').run(id) this.touchNotebook(cell.notebook_id) diff --git a/apps/desktop/src/preload/index.d.ts b/apps/desktop/src/preload/index.d.ts index 721f52d..b2f235f 100644 --- a/apps/desktop/src/preload/index.d.ts +++ b/apps/desktop/src/preload/index.d.ts @@ -430,9 +430,7 @@ interface DataPeekApi { onEvent: (callback: (event: PgNotificationEvent) => void) => () => void onStatus: (callback: (status: PgNotificationConnectionStatus) => void) => () => void reconnect: (connectionId: string) => Promise> - getStatus: ( - connectionId: string - ) => Promise> + getStatus: (connectionId: string) => Promise> getAllStatuses: () => Promise> } health: { @@ -466,11 +464,11 @@ interface DataPeekApi { list: () => Promise> get: (id: string) => Promise> create: (input: CreateNotebookInput) => Promise> - update: (id: string, updates: UpdateNotebookInput) => Promise> + update: (id: string, updates: UpdateNotebookInput) => Promise> delete: (id: string) => Promise> - duplicate: (id: string, connectionId: string) => Promise> + duplicate: (id: string, connectionId: string) => Promise> addCell: (notebookId: string, input: AddCellInput) => Promise> - updateCell: (cellId: string, updates: UpdateCellInput) => Promise> + updateCell: (cellId: string, updates: UpdateCellInput) => Promise> deleteCell: (cellId: string) => Promise> reorderCells: (notebookId: string, cellIds: string[]) => Promise> } diff --git a/apps/desktop/src/preload/index.ts b/apps/desktop/src/preload/index.ts index d11fd95..d76872a 100644 --- a/apps/desktop/src/preload/index.ts +++ b/apps/desktop/src/preload/index.ts @@ -319,21 +319,19 @@ const api = { }, // Notebooks management notebooks: { - list: (): Promise> => - ipcRenderer.invoke('notebooks:list'), + list: (): Promise> => ipcRenderer.invoke('notebooks:list'), get: (id: string): Promise> => ipcRenderer.invoke('notebooks:get', id), create: (input: CreateNotebookInput): Promise> => ipcRenderer.invoke('notebooks:create', input), - update: (id: string, updates: UpdateNotebookInput): Promise> => + update: (id: string, updates: UpdateNotebookInput): Promise> => ipcRenderer.invoke('notebooks:update', { id, updates }), - delete: (id: string): Promise> => - ipcRenderer.invoke('notebooks:delete', id), - duplicate: (id: string, connectionId: string): Promise> => + delete: (id: string): Promise> => ipcRenderer.invoke('notebooks:delete', id), + duplicate: (id: string, connectionId: string): Promise> => ipcRenderer.invoke('notebooks:duplicate', { id, connectionId }), addCell: (notebookId: string, input: AddCellInput): Promise> => ipcRenderer.invoke('notebooks:add-cell', { notebookId, input }), - updateCell: (cellId: string, updates: UpdateCellInput): Promise> => + updateCell: (cellId: string, updates: UpdateCellInput): Promise> => ipcRenderer.invoke('notebooks:update-cell', { cellId, updates }), deleteCell: (cellId: string): Promise> => ipcRenderer.invoke('notebooks:delete-cell', cellId), @@ -572,11 +570,8 @@ const api = { ipcRenderer.on('pg-notify:event', handler) return () => ipcRenderer.removeListener('pg-notify:event', handler) }, - onStatus: ( - callback: (status: PgNotificationConnectionStatus) => void - ): (() => void) => { - const handler = (_: unknown, status: PgNotificationConnectionStatus): void => - callback(status) + onStatus: (callback: (status: PgNotificationConnectionStatus) => void): (() => void) => { + const handler = (_: unknown, status: PgNotificationConnectionStatus): void => callback(status) ipcRenderer.on('pg-notify:status', handler) return () => ipcRenderer.removeListener('pg-notify:status', handler) }, diff --git a/apps/desktop/src/renderer/src/components/notebook-editor.tsx b/apps/desktop/src/renderer/src/components/notebook-editor.tsx index 2443a94..91f49e7 100644 --- a/apps/desktop/src/renderer/src/components/notebook-editor.tsx +++ b/apps/desktop/src/renderer/src/components/notebook-editor.tsx @@ -1,5 +1,5 @@ import { useState, useCallback, useEffect, useRef } from 'react' -import { Plus, Play } from 'lucide-react' +import { Plus } from 'lucide-react' import { Button, cn } from '@data-peek/ui' import { useNotebookStore } from '@/stores/notebook-store' import { useConnectionStore } from '@/stores/connection-store' @@ -58,9 +58,7 @@ export function NotebookEditor({ tab }: NotebookEditorProps) { ? cells[insertAfterIndex].order + 0.5 : cells.length addCell(activeNotebook.id, { type, content: '', order }) - setFocusedCellIndex( - insertAfterIndex !== undefined ? insertAfterIndex + 1 : cells.length - ) + setFocusedCellIndex(insertAfterIndex !== undefined ? insertAfterIndex + 1 : cells.length) }, [activeNotebook, cells, addCell] ) @@ -202,24 +200,12 @@ export function NotebookEditor({ tab }: NotebookEditorProps) { Note - -
{cells.length === 0 ? (
-

- Empty notebook. Add your first cell. -

+

Empty notebook. Add your first cell.