diff --git a/lib/livebook/session.ex b/lib/livebook/session.ex
index 7570e9472..97b205f14 100644
--- a/lib/livebook/session.ex
+++ b/lib/livebook/session.ex
@@ -242,6 +242,14 @@ defmodule Livebook.Session do
GenServer.cast(name(session_id), :save)
end
+ @doc """
+ Synchronous version of `save/1`.
+ """
+ @spec save_sync(id()) :: :ok
+ def save_sync(session_id) do
+ GenServer.call(name(session_id), :save)
+ end
+
@doc """
Asynchronously sends a close request to the server.
@@ -316,6 +324,10 @@ defmodule Livebook.Session do
{:reply, summary_from_state(state), state}
end
+ def handle_call(:save, _from, state) do
+ {:reply, :ok, maybe_save_notebook(state)}
+ end
+
@impl true
def handle_cast({:insert_section, client_pid, index}, state) do
# Include new id in the operation, so it's reproducible
diff --git a/lib/livebook_web/live/home_live.ex b/lib/livebook_web/live/home_live.ex
index 5c3c61802..fd5b57bd6 100644
--- a/lib/livebook_web/live/home_live.ex
+++ b/lib/livebook_web/live/home_live.ex
@@ -43,7 +43,8 @@ defmodule LivebookWeb.HomeLive do
path: @path,
extnames: [LiveMarkdown.extension()],
running_paths: paths(@session_summaries),
- target: nil do %>
+ phx_target: nil,
+ phx_submit: nil do %>
<%= content_tag :button,
class: "button button-outlined-gray",
diff --git a/lib/livebook_web/live/path_select_component.ex b/lib/livebook_web/live/path_select_component.ex
index 371c26d8f..afc28521d 100644
--- a/lib/livebook_web/live/path_select_component.ex
+++ b/lib/livebook_web/live/path_select_component.ex
@@ -5,8 +5,9 @@ defmodule LivebookWeb.PathSelectComponent do
#
# * `path` - the currently entered path
# * `running_paths` - the list of notebook paths that are already linked to running sessions
- # * `target` - id of the component to send update events to or nil to send to the parent LV
# * `extnames` - a list of file extensions that should be shown
+ # * `phx_target` - id of the component to send update events to or nil to send to the parent LV
+ # * `phx_submit` - the event name sent on form submission, use `nil` for no action
#
# The target receives `set_path` events with `%{"path" => path}` payload.
#
@@ -26,8 +27,12 @@ defmodule LivebookWeb.PathSelectComponent do
@@ -55,7 +60,7 @@ defmodule LivebookWeb.PathSelectComponent do
"""
end
- defp render_file(file, target) do
+ defp render_file(file, phx_target) do
icon =
case file do
%{is_running: true} -> "play-circle-line"
@@ -69,7 +74,7 @@ defmodule LivebookWeb.PathSelectComponent do
<%= content_tag :button, if(matching_runtime?(@current_runtime, @path), do: "Reconnect", else: "Connect"),
class: "button button-blue",
phx_click: "init",
- disabled: not mix_project_root?(@path) %>
+ disabled: disabled?(@path) %>
<% end %>
<%= if @status != :initial do %>
@@ -58,12 +59,6 @@ defmodule LivebookWeb.SessionLive.MixStandaloneLive do
"""
end
- defp matching_runtime?(%Runtime.MixStandalone{} = runtime, path) do
- Path.expand(runtime.project_path) == Path.expand(path)
- end
-
- defp matching_runtime?(_runtime, _path), do: false
-
@impl true
def handle_event("set_path", %{"path" => path}, socket) do
{:noreply, assign(socket, path: path)}
@@ -109,4 +104,14 @@ defmodule LivebookWeb.SessionLive.MixStandaloneLive do
defp mix_project_root?(path) do
File.dir?(path) and File.exists?(Path.join(path, "mix.exs"))
end
+
+ defp matching_runtime?(%Runtime.MixStandalone{} = runtime, path) do
+ Path.expand(runtime.project_path) == Path.expand(path)
+ end
+
+ defp matching_runtime?(_runtime, _path), do: false
+
+ defp disabled?(path) do
+ not mix_project_root?(path)
+ end
end
diff --git a/lib/livebook_web/live/session_live/persistence_component.ex b/lib/livebook_web/live/session_live/persistence_component.ex
index 41cc33112..c534cb51d 100644
--- a/lib/livebook_web/live/session_live/persistence_component.ex
+++ b/lib/livebook_web/live/session_live/persistence_component.ex
@@ -36,7 +36,8 @@ defmodule LivebookWeb.SessionLive.PersistenceComponent do
path: @path,
extnames: [LiveMarkdown.extension()],
running_paths: @running_paths,
- target: @myself %>
+ phx_target: @myself,
+ phx_submit: if(disabled?(@path, @current_path, @running_paths), do: nil, else: "save") %>