repl: Show add-cell controls in empty notebooks (#61329)

## Objective

When a notebook has no cells, the editor renders a blank pane with no
visible way to add one. Cells can only be added via the toolbar icons or
keybindings, which aren't discoverable from an empty editor — so a newly
created `.ipynb` looks broken.

This is the follow-up direction suggested in #61296.

## Solution

Render an empty-state in the editor body when the notebook has no cells,
with "Add code cell" / "Add markdown cell" buttons (dispatching the
existing `add_code_block` / `add_markdown_block` handlers), matching the
affordance VSCode provides.

## Testing

Tested locally on macOS with the notebook feature enabled
(`LOCAL_NOTEBOOK_DEV=1`):

- Opening an empty `.ipynb` shows the empty-state instead of a blank
pane.
- Clicking "Add code cell" inserts an editable code cell and marks the
tab dirty.

## Showcase

**Before** — empty notebook renders a blank pane:

![Before: blank
pane](https://raw.githubusercontent.com/ArneshBanerjee/zed-jupyter/pr-assets/61329-before.png)

**After** — empty notebook shows add-cell controls:

![After: add-cell
controls](https://raw.githubusercontent.com/ArneshBanerjee/zed-jupyter/pr-assets/61329-after.png)

Clicking "Add code cell" inserts a cell:

![After clicking Add code
cell](https://raw.githubusercontent.com/ArneshBanerjee/zed-jupyter/pr-assets/61329-after-add-cell.png)

Release Notes:

- N/A

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Finn Evers <finn.evers@outlook.de>
This commit is contained in:
authored and GitHub committed 2026-07-24 11:08:09 +00:00
1 parent 90b1549310
commit d1548bd013
1 file changed
+49 -2
+49 -2
View File
@@ -18,7 +18,7 @@ use language::{Language, LanguageRegistry};
use log;
use project::{Project, ProjectEntryId, ProjectPath};
use settings::Settings as _;
use ui::{CommonAnimationExt, Tooltip, prelude::*};
use ui::{CommonAnimationExt, KeyBinding, Tooltip, prelude::*};
use workspace::item::{ItemEvent, SaveOptions, TabContentParams};
use workspace::searchable::SearchableItemHandle;
use workspace::{Item, ItemHandle, Pane, ProjectItem, ToolbarItemLocation};
@@ -1278,6 +1278,44 @@ impl NotebookEditor {
.size_full()
}
fn render_empty_state(&self, cx: &mut Context<Self>) -> impl IntoElement {
v_flex()
.size_full()
.items_center()
.justify_center()
.gap_3()
.child(Label::new("This notebook is empty.").color(Color::Muted))
.child(
h_flex()
.gap_2()
.child(
Button::new("empty-state-add-code", "Add code cell")
.start_icon(Icon::new(IconName::Code))
.key_binding(KeyBinding::for_action_in(
&AddCodeBlock,
&self.focus_handle,
cx,
))
.on_click(
cx.listener(|this, _, window, cx| this.add_code_block(window, cx)),
),
)
.child(
Button::new("empty-state-add-markdown", "Add markdown cell")
.style(ButtonStyle::Subtle)
.start_icon(Icon::new(IconName::FileMarkdown))
.key_binding(KeyBinding::for_action_in(
&AddMarkdownBlock,
&self.focus_handle,
cx,
))
.on_click(cx.listener(|this, _, window, cx| {
this.add_markdown_block(window, cx)
})),
),
)
}
fn cell_position(&self, index: usize) -> CellPosition {
match index {
0 => CellPosition::First,
@@ -1477,7 +1515,16 @@ impl Render for NotebookEditor {
.w_full()
.h_full()
.gap_2()
.child(div().flex_1().h_full().child(self.cell_list(window, cx)))
.child(
div()
.flex_1()
.h_full()
.child(if self.cell_order.is_empty() {
self.render_empty_state(cx).into_any_element()
} else {
self.cell_list(window, cx).into_any_element()
}),
)
.child(self.render_notebook_controls(window, cx)),
)
.child(self.render_kernel_status_bar(window, cx))