Skip to content

Commit

Permalink
Merge pull request #22 from nteract/expanded-cell-creation
Browse files Browse the repository at this point in the history
Expanded cell creation
  • Loading branch information
rgbkrk authored Feb 3, 2024
2 parents 46b5ae5 + 41a60ee commit c6da527
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 49 deletions.
28 changes: 20 additions & 8 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use serde_json::json;
use tokio::sync::Mutex;

use std::collections::HashMap;
Expand Down Expand Up @@ -94,20 +95,26 @@ impl AppState {
}

// Return cell_id
async fn create_cell(&self, window_id: &str) -> Option<String> {
async fn create_cell(&self, window: Window, cell_type: Option<&str>) -> Option<String> {
let window_id = window.label();
debug!("Creating a new cell in window with ID: {}", window_id);

let cell_type = cell_type.unwrap_or("code");
let mut notebooks = self.notebooks.lock().await;
if let Some(notebook) = notebooks.get_mut(window_id) {
let new_cell = notebook.add_code_cell("");
let new_cell = match cell_type {
"markdown" => notebook.add_markdown_cell(""),
_ => notebook.add_code_cell(""),
};
Some(new_cell.id().to_string())
} else {
None
}
}

// Update a specific cell within the specified notebook
async fn update_cell(&self, window_id: &str, cell_id: &str, new_content: &str) -> bool {
async fn update_cell(&self, window: Window, cell_id: &str, new_content: &str) -> bool {
let window_id = window.label();
debug!(
"Updating cell with ID: {} in window with ID: {} with new content: {}",
cell_id, window_id, new_content
Expand All @@ -117,6 +124,13 @@ impl AppState {
if let Some(notebook) = notebooks.get_mut(window_id) {
if let Some(cell) = notebook.get_mut_cell(cell_id) {
cell.set_source(new_content);

window.emit(
format!("cell-{}", cell_id).as_str(),
Some(json!({
"source": new_content
}))
).unwrap();
}
true
} else {
Expand All @@ -126,9 +140,8 @@ impl AppState {
}

#[tauri::command]
async fn create_cell(state: State<'_, AppState>, window: Window) -> Result<Option<String>, String> {
let window_id = window.label(); // Use the window label as a unique identifier
Ok(state.create_cell(window_id).await)
async fn create_cell(state: State<'_, AppState>, window: Window, cell_type: &str) -> Result<Option<String>, String> {
Ok(state.create_cell(window, Some(cell_type)).await)
}

#[tauri::command]
Expand All @@ -147,8 +160,7 @@ async fn update_cell(
cell_id: &str,
new_content: &str,
) -> Result<bool, String> {
let window_id = window.label(); // Use the window label as a unique identifier
Ok(state.update_cell(window_id, cell_id, new_content).await)
Ok(state.update_cell(window, cell_id, new_content).await)
}

// The main entry point for the Tauri application
Expand Down
25 changes: 17 additions & 8 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
import Cell from "@/components/Cell";

import { Button} from "@/components/ui/button";
import { Button } from "@/components/ui/button";
import { useNotebook } from '@/hooks/useNotebook';

import { MarkdownCell } from "@/components/markdown-cell"
import { CodeCell } from '@/components/code-cell'

function App() {
const { cells, createCell } = useNotebook();
const { cells, createCodeCell, createMarkdownCell } = useNotebook();
return (
<div>
{cells.map((cellId: string) => (
<Cell cellId={cellId} key={cellId}/>
))}
<Button onClick={createCell}>New Cell</Button>
{cells.map((cellInfo) => {
switch (cellInfo.cellType) {
case "code":
return <CodeCell cellId={cellInfo.id} key={cellInfo.id} />
case "markdown":
return <MarkdownCell cellId={cellInfo.id} key={cellInfo.id} />
default:
return "Unknown cell type"
}
})}
<Button onClick={createCodeCell}>New Code Cell</Button>
<Button onClick={createMarkdownCell}>New Markdown Cell</Button>
</div>
);
}
Expand Down
9 changes: 0 additions & 9 deletions src/components/Cell.tsx

This file was deleted.

4 changes: 2 additions & 2 deletions src/components/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { lightTheme } from "@/codemirror-themes";

import { StateField } from "@codemirror/state";
import { EditorView, keymap } from "@codemirror/view";
import { useCodeCell } from "@/hooks/useCell";
import { useCell } from "@/hooks/useCell";

import { invoke } from "@tauri-apps/api/tauri";

Expand Down Expand Up @@ -82,7 +82,7 @@ const baseExtensions = [
export const Editor = ({ cellId, className, language }: { cellId: string, className?: string, language: string }) => {
const ref = useRef<HTMLDivElement>(null);

const { content, updateCell } = useCodeCell(cellId);
const { content, updateCell } = useCell(cellId);

// We need to compute a derived extensions state based on the language of the editor
const extensions = useMemo(() => {
Expand Down
10 changes: 10 additions & 0 deletions src/components/markdown-cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { useRemark } from "react-remark";

import { useMarkdownCell } from "@/hooks/useCell";

import { Editor } from "@/components//Editor";

export const MarkdownCell = ({ cellId }: { cellId: string }) => {
const { content } = useMarkdownCell(cellId);

Expand All @@ -12,13 +14,21 @@ export const MarkdownCell = ({ cellId }: { cellId: string }) => {
setMarkdownSource(content);
}, [content]);

console.log(content)


return (
<div className="flex items-start">
<div className="w-14 h-full pt-1 pb-1 m-0">
{/* placeholder */}
</div>

<Editor
cellId={cellId}
className="mr-2 pt-0 pb-0 text-sm"
language="markdown"
/>

<div className="prose">{reactContent}</div>
</div>
);
Expand Down
44 changes: 30 additions & 14 deletions src/hooks/useCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,43 @@ function cellReducer(state: CellState, action: Action) {
}
}

export function useMarkdownCell(cellId: string) {
export function useCell(cellId: string) {
const [content, setContent] = useState<string>("");

const updateCell = useCallback(async (newContent: string) => {
try {
await invoke("update_cell", { cellId, newContent });
setContent(newContent);
await invoke("update_cell", { cellId, newContent });
} catch (error) {
console.error(error);
// Handle error
}
}, [cellId]);
}, [cellId]);


useEffect(() => {
const setupListener = async () => {
const unlisten = await listen(`cell-${cellId}`, (event) => {
const cellUpdate = event.payload as any;

setContent(cellUpdate.source)
});

return () => {
unlisten();
};
};

setupListener();
}, [cellId]);



return { content, setContent, updateCell }
}

export function useMarkdownCell(cellId: string) {
const { content, updateCell } = useCell(cellId);

// TODO: Push/pull this from cell metadata
const metadata = { };
Expand All @@ -55,7 +80,8 @@ export function useMarkdownCell(cellId: string) {
}

export function useCodeCell(cellId: string) {
const [content, setContent] = useState<string>("");
const { content, updateCell } = useCell(cellId);

const [state, dispatch] = useReducer(cellReducer, initialState);

const executeCell = useCallback(async () => {
Expand All @@ -69,16 +95,6 @@ export function useCodeCell(cellId: string) {
dispatch({ type: 'reset' });
}, [cellId]);

const updateCell = useCallback(async (newContent: string) => {
try {
await invoke("update_cell", { cellId, newContent });
setContent(newContent);
} catch (error) {
console.error(error);
// Handle error
}
}, [cellId]);

useEffect(() => {
const setupListener = async () => {
const unlisten = await listen(`cell-outputs-${cellId}`, (event) => {
Expand Down
22 changes: 14 additions & 8 deletions src/hooks/useNotebook.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { useState } from 'react';
import { useState, useCallback } from 'react';
import { invoke } from "@tauri-apps/api/tauri";

type CellInfo = {
id: string,
cellType: "code" | "markdown"
}

export function useNotebook() {
const [cells, setCells] = useState<string[]>([]);
const [cells, setCells] = useState<CellInfo[]>([]);

async function createCell() {
const id = (await invoke("create_cell")) as string;
setCells(oldCells => [...oldCells, id]);
}
const createCell = useCallback(async (cellType: "code" | "markdown") => {
const id = (await invoke("create_cell", { cellType })) as string;
setCells(oldCells => [...oldCells, { id, cellType }]);
}, []);

// TODO(kyle): Listen to events from the backend to update the cell list
const createCodeCell = useCallback(() => createCell("code"), [createCell]);
const createMarkdownCell = useCallback(() => createCell("markdown"), [createCell]);

return { cells, createCell };
return { cells, createCell, createCodeCell, createMarkdownCell};
}

0 comments on commit c6da527

Please sign in to comment.