defmodule LivebookWeb.SettingsLive do
use LivebookWeb, :live_view
alias LivebookWeb.{LayoutHelpers, PageHelpers}
on_mount LivebookWeb.SidebarHook
@impl true
def mount(_params, _session, socket) do
if connected?(socket) do
Livebook.Settings.subscribe()
end
{:ok,
assign(socket,
file_systems: Livebook.Settings.file_systems(),
env_vars: Livebook.Settings.fetch_env_vars() |> Enum.sort(),
env_var: nil,
autosave_path_state: %{
file: autosave_dir(),
dialog_opened?: false
},
update_check_enabled: Livebook.UpdateCheck.enabled?(),
page_title: "Livebook - Settings",
default_file_system_id: Livebook.Settings.default_file_system_id()
)}
end
@impl true
def render(assigns) do
~H"""
Here you can change global Livebook configuration. Keep in mind
that this configuration gets persisted and will be restored on application
launch.
About
<%= if app_name = Livebook.Config.app_service_name() do %>
<.labeled_text label="Application">
<%= if app_url = Livebook.Config.app_service_url() do %>
<%= app_name %>
<% else %>
<%= app_name %>
<% end %>
<% end %>
<.labeled_text label="Livebook">
v<%= Application.spec(:livebook, :vsn) %>
<.labeled_text label="Elixir">
v<%= System.version() %>
<%= live_redirect to: Routes.live_dashboard_path(@socket, :home),
class: "button-base button-outlined-gray" do %>
<.remix_icon icon="dashboard-2-line" class="align-middle mr-1" />
Open dashboard
<% end %>
Updates
Autosave
The directory to keep unsaved notebooks.
<.autosave_path_select state={@autosave_path_state} />
File systems
File systems are used to store notebooks. The local disk filesystem
is visible only to the current machine, but alternative file systems
are available, such as S3-based storages.
Environment variables
Environment variables store global values which are available
inside your notebooks. You can also configure the PATH
environment to make system dependencies available to notebooks.
<.live_component
module={LivebookWeb.EnvVarsComponent}
id="env-vars"
env_vars={@env_vars}
return_to={Routes.settings_path(@socket, :page)}
add_env_var_path={Routes.settings_path(@socket, :add_env_var)}
/>
The configuration in this section changes only your Livebook
experience and is saved in your browser.
Code editor
<.switch_checkbox
name="editor_auto_completion"
label="Show completion list while typing"
checked={false}
/>
<.switch_checkbox
name="editor_auto_signature"
label="Show function signature while typing"
checked={false}
/>
<.switch_checkbox name="editor_font_size" label="Increase font size" checked={false} />
<.switch_checkbox
name="editor_high_contrast"
label="Use high contrast theme"
checked={false}
/>
<.switch_checkbox
name="editor_markdown_word_wrap"
label="Wrap words in Markdown"
checked={false}
/>
<%= if @live_action == :add_file_system do %>
<.modal
id="add-file-system-modal"
show
class="w-full max-w-3xl"
patch={Routes.settings_path(@socket, :page)}
>
<.live_component
module={LivebookWeb.SettingsLive.AddFileSystemComponent}
id="add-file-system"
return_to={Routes.settings_path(@socket, :page)}
/>
<% end %>
<%= if @live_action in [:add_env_var, :edit_env_var] do %>
<.modal
id="env-var-modal"
show
class="w-full max-w-3xl"
on_close={JS.push("clear_env_var")}
patch={Routes.settings_path(@socket, :page)}
>
<.live_component
module={LivebookWeb.EnvVarComponent}
id="env-var"
env_var={@env_var}
headline="Configure your application global environment variables."
return_to={Routes.settings_path(@socket, :page)}
/>
<% end %>
"""
end
defp autosave_path_select(%{state: %{file: nil}} = assigns), do: ~H""
defp autosave_path_select(%{state: %{dialog_opened?: true}} = assigns) do
~H"""
<.live_component
module={LivebookWeb.FileSelectComponent}
id="autosave-path-component"
file={@state.file}
extnames={[]}
running_files={[]}
submit_event={:set_autosave_path}
file_system_select_disabled={true}
>
Cancel
Reset
Save
"""
end
defp autosave_path_select(assigns) do
~H"""
Change
"""
end
@impl true
def handle_params(%{"env_var_id" => key}, _url, socket) do
env_var = Livebook.Settings.fetch_env_var!(key)
{:noreply, assign(socket, env_var: env_var)}
end
def handle_params(%{"file_system_id" => file_system_id}, _url, socket) do
{:noreply, assign(socket, file_system_id: file_system_id)}
end
def handle_params(_params, _url, socket), do: {:noreply, assign(socket, env_var: nil)}
@impl true
def handle_event("cancel_autosave_path", %{}, socket) do
{:noreply,
update(
socket,
:autosave_path_state,
&%{&1 | dialog_opened?: false, file: autosave_dir()}
)}
end
def handle_event("set_autosave_path", %{}, socket) do
path = socket.assigns.autosave_path_state.file.path
Livebook.Settings.set_autosave_path(path)
{:noreply,
update(
socket,
:autosave_path_state,
&%{&1 | dialog_opened?: false, file: autosave_dir()}
)}
end
@impl true
def handle_event("reset_autosave_path", %{}, socket) do
{:noreply,
update(
socket,
:autosave_path_state,
&%{&1 | file: default_autosave_dir()}
)}
end
def handle_event("open_autosave_path_select", %{}, socket) do
{:noreply, update(socket, :autosave_path_state, &%{&1 | dialog_opened?: true})}
end
def handle_event("detach_file_system", %{"id" => file_system_id}, socket) do
Livebook.Settings.remove_file_system(file_system_id)
file_systems = Livebook.Settings.file_systems()
{:noreply, assign(socket, file_systems: file_systems)}
end
def handle_event("make_default_file_system", %{"id" => file_system_id}, socket) do
Livebook.Settings.set_default_file_system(file_system_id)
{:noreply, assign(socket, default_file_system_id: file_system_id)}
end
def handle_event("save", %{"update_check_enabled" => enabled}, socket) do
enabled = enabled == "true"
Livebook.UpdateCheck.set_enabled(enabled)
{:noreply, assign(socket, :update_check_enabled, enabled)}
end
def handle_event("save", %{"env_var" => attrs}, socket) do
env_var = %Livebook.Settings.EnvVar{}
case Livebook.Settings.set_env_var(socket.assigns.env_var || env_var, attrs) do
{:ok, _} ->
{:noreply, push_patch(socket, to: Routes.settings_path(socket, :page))}
{:error, _changeset} ->
{:noreply, socket}
end
end
def handle_event("edit_env_var", %{"env_var" => key}, socket) do
{:noreply, push_patch(socket, to: Routes.settings_path(socket, :edit_env_var, key))}
end
def handle_event("delete_env_var", %{"env_var" => key}, socket) do
Livebook.Settings.unset_env_var(key)
{:noreply, socket}
end
@impl true
def handle_info({:file_systems_updated, file_systems}, socket) do
{:noreply, assign(socket, file_systems: file_systems)}
end
def handle_info({:set_file, file, _info}, socket) do
{:noreply, update(socket, :autosave_path_state, &%{&1 | file: file})}
end
def handle_info(:set_autosave_path, socket) do
handle_event("set_autosave_path", %{}, socket)
end
def handle_info({:env_var_set, env_var}, socket) do
idx = Enum.find_index(socket.assigns.env_vars, &(&1.name == env_var.name))
env_vars =
if idx,
do: List.replace_at(socket.assigns.env_vars, idx, env_var),
else: [env_var | socket.assigns.env_vars]
{:noreply, assign(socket, env_vars: Enum.sort(env_vars), env_var: nil)}
end
def handle_info({:env_var_unset, env_var}, socket) do
env_vars = Enum.reject(socket.assigns.env_vars, &(&1.name == env_var.name))
{:noreply, assign(socket, env_vars: env_vars, env_var: nil)}
end
def handle_info(_message, socket), do: {:noreply, socket}
defp autosave_dir() do
if path = Livebook.Settings.autosave_path() do
path
|> Livebook.FileSystem.Utils.ensure_dir_path()
|> Livebook.FileSystem.File.local()
end
end
defp default_autosave_dir() do
Livebook.Settings.default_autosave_path()
|> Livebook.FileSystem.Utils.ensure_dir_path()
|> Livebook.FileSystem.File.local()
end
end