diff --git a/Cargo.lock b/Cargo.lock
index c04ec535a6041d2886fa3ff26378dba3e059df3b..4d95eee0980b78500b6257ec9e8e4323916114c3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1586,7 +1586,7 @@ dependencies = [
"bitflags 2.6.0",
"cexpr",
"clang-sys",
- "itertools 0.10.5",
+ "itertools 0.12.1",
"lazy_static",
"lazycell",
"proc-macro2",
@@ -5584,7 +5584,7 @@ dependencies = [
"httpdate",
"itoa",
"pin-project-lite",
- "socket2 0.4.10",
+ "socket2 0.5.7",
"tokio",
"tower-service",
"tracing",
@@ -6154,6 +6154,20 @@ dependencies = [
"simple_asn1",
]
+[[package]]
+name = "jupyter-serde"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a444fb3f87ee6885eb316028cc998c7d84811663ef95d78c419419423d5a054"
+dependencies = [
+ "anyhow",
+ "chrono",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "uuid",
+]
+
[[package]]
name = "khronos-egl"
version = "6.0.0"
@@ -6474,7 +6488,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
dependencies = [
"cfg-if",
- "windows-targets 0.48.5",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -7137,6 +7151,21 @@ dependencies = [
"tempfile",
]
+[[package]]
+name = "nbformat"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "146074ad45cab20f5d98ccded164826158471f21d04f96e40b9872529e10979d"
+dependencies = [
+ "anyhow",
+ "chrono",
+ "jupyter-serde",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "uuid",
+]
+
[[package]]
name = "ndk"
version = "0.8.0"
@@ -9579,6 +9608,7 @@ dependencies = [
"command_palette_hooks",
"editor",
"env_logger 0.11.5",
+ "feature_flags",
"futures 0.3.30",
"gpui",
"http_client",
@@ -9588,7 +9618,9 @@ dependencies = [
"languages",
"log",
"markdown_preview",
+ "menu",
"multi_buffer",
+ "nbformat",
"project",
"runtimelib",
"schemars",
@@ -9927,9 +9959,9 @@ dependencies = [
[[package]]
name = "runtimelib"
-version = "0.15.0"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7d76d28b882a7b889ebb04e79bc2b160b3061821ea596ff0f4a838fc7a76db0"
+checksum = "263588fe9593333c4bfde258c9021fc64e766ea434e070c6b67c7100536d6499"
dependencies = [
"anyhow",
"async-dispatcher",
@@ -9941,6 +9973,7 @@ dependencies = [
"dirs 5.0.1",
"futures 0.3.30",
"glob",
+ "jupyter-serde",
"rand 0.8.5",
"ring 0.17.8",
"serde",
@@ -14126,7 +14159,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 0697cc0c0becc472a71ee9ddc640c04404122b54..e269dd99ea71a70ae8d02216ad1dc901561fc7c6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -371,6 +371,7 @@ linkify = "0.10.0"
log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
markup5ever_rcdom = "0.3.0"
nanoid = "0.4"
+nbformat = "0.3.1"
nix = "0.29"
num-format = "0.4.4"
once_cell = "1.19.0"
@@ -402,7 +403,7 @@ reqwest = { git = "https://github.com/zed-industries/reqwest.git", rev = "fd110f
"stream",
] }
rsa = "0.9.6"
-runtimelib = { version = "0.15", default-features = false, features = [
+runtimelib = { version = "0.16.0", default-features = false, features = [
"async-dispatcher-runtime",
] }
rustc-demangle = "0.1.23"
diff --git a/assets/icons/list_x.svg b/assets/icons/list_x.svg
new file mode 100644
index 0000000000000000000000000000000000000000..683f38ab5dfe5b7f30cb507bd9e4679321fc774a
--- /dev/null
+++ b/assets/icons/list_x.svg
@@ -0,0 +1,7 @@
+
diff --git a/crates/feature_flags/src/feature_flags.rs b/crates/feature_flags/src/feature_flags.rs
index fb4e192023d914f9ea651e838b7678b548d329d8..286acdfc98e6cc51ec3b77f86acb0a7d1b37e379 100644
--- a/crates/feature_flags/src/feature_flags.rs
+++ b/crates/feature_flags/src/feature_flags.rs
@@ -59,6 +59,12 @@ impl FeatureFlag for ZedPro {
const NAME: &'static str = "zed-pro";
}
+pub struct NotebookFeatureFlag;
+
+impl FeatureFlag for NotebookFeatureFlag {
+ const NAME: &'static str = "notebooks";
+}
+
pub struct AutoCommand {}
impl FeatureFlag for AutoCommand {
const NAME: &'static str = "auto-command";
diff --git a/crates/repl/Cargo.toml b/crates/repl/Cargo.toml
index 001bf157d54c05693c1db4d4a2e7ba943dcede26..f035878d332654e321f8447c2bdc0ee9a964d6c7 100644
--- a/crates/repl/Cargo.toml
+++ b/crates/repl/Cargo.toml
@@ -21,13 +21,16 @@ client.workspace = true
collections.workspace = true
command_palette_hooks.workspace = true
editor.workspace = true
+feature_flags.workspace = true
futures.workspace = true
gpui.workspace = true
image.workspace = true
language.workspace = true
log.workspace = true
markdown_preview.workspace = true
+menu.workspace = true
multi_buffer.workspace = true
+nbformat.workspace = true
project.workspace = true
runtimelib.workspace = true
schemars.workspace = true
diff --git a/crates/repl/src/notebook.rs b/crates/repl/src/notebook.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9c6738f79979e8b4b5bed749ebee7993cc509b97
--- /dev/null
+++ b/crates/repl/src/notebook.rs
@@ -0,0 +1,4 @@
+mod cell;
+mod notebook_ui;
+pub use cell::*;
+pub use notebook_ui::*;
diff --git a/crates/repl/src/notebook/cell.rs b/crates/repl/src/notebook/cell.rs
new file mode 100644
index 0000000000000000000000000000000000000000..bb6b6fbf3865b37d7e701b64fe9fe00a5a5c49cd
--- /dev/null
+++ b/crates/repl/src/notebook/cell.rs
@@ -0,0 +1,733 @@
+#![allow(unused, dead_code)]
+use std::sync::Arc;
+
+use editor::{Editor, EditorMode, MultiBuffer};
+use futures::future::Shared;
+use gpui::{prelude::*, AppContext, Hsla, Task, TextStyleRefinement, View};
+use language::{Buffer, Language, LanguageRegistry};
+use markdown_preview::{markdown_parser::parse_markdown, markdown_renderer::render_markdown_block};
+use nbformat::v4::{CellId, CellMetadata, CellType};
+use settings::Settings as _;
+use theme::ThemeSettings;
+use ui::{prelude::*, IconButtonShape};
+use util::ResultExt;
+
+use crate::{
+ notebook::{CODE_BLOCK_INSET, GUTTER_WIDTH},
+ outputs::{plain::TerminalOutput, user_error::ErrorView, Output},
+};
+
+#[derive(Copy, Clone, PartialEq, PartialOrd)]
+pub enum CellPosition {
+ First,
+ Middle,
+ Last,
+}
+
+pub enum CellControlType {
+ RunCell,
+ RerunCell,
+ ClearCell,
+ CellOptions,
+ CollapseCell,
+ ExpandCell,
+}
+
+impl CellControlType {
+ fn icon_name(&self) -> IconName {
+ match self {
+ CellControlType::RunCell => IconName::Play,
+ CellControlType::RerunCell => IconName::ArrowCircle,
+ CellControlType::ClearCell => IconName::ListX,
+ CellControlType::CellOptions => IconName::Ellipsis,
+ CellControlType::CollapseCell => IconName::ChevronDown,
+ CellControlType::ExpandCell => IconName::ChevronRight,
+ }
+ }
+}
+
+pub struct CellControl {
+ button: IconButton,
+}
+
+impl CellControl {
+ fn new(id: impl Into, control_type: CellControlType) -> Self {
+ let icon_name = control_type.icon_name();
+ let id = id.into();
+ let button = IconButton::new(id, icon_name)
+ .icon_size(IconSize::Small)
+ .shape(IconButtonShape::Square);
+ Self { button }
+ }
+}
+
+impl Clickable for CellControl {
+ fn on_click(self, handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static) -> Self {
+ let button = self.button.on_click(handler);
+ Self { button }
+ }
+
+ fn cursor_style(self, _cursor_style: gpui::CursorStyle) -> Self {
+ self
+ }
+}
+
+/// A notebook cell
+#[derive(Clone)]
+pub enum Cell {
+ Code(View),
+ Markdown(View),
+ Raw(View),
+}
+
+fn convert_outputs(outputs: &Vec, cx: &mut WindowContext) -> Vec