defmodule LivebookWeb.SettingsLive do
use LivebookWeb, :live_view
alias LivebookWeb.LayoutHelpers
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: "Settings - Livebook",
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
<.labeled_text :if={app_name = Livebook.Config.app_service_name()} label="Application">
<%= if app_url = Livebook.Config.app_service_url() do %>
<%= app_name %>
<% else %>
<%= app_name %>
<% end %>
<.labeled_text label="Livebook">
v<%= Application.spec(:livebook, :vsn) %>
<.labeled_text label="Elixir">
v<%= System.version() %>
<.link navigate={~p"/dashboard"} class="button-base button-outlined-gray">
<.remix_icon icon="dashboard-2-line" class="align-middle mr-1" />
Open dashboard
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 file system
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, specific to this
Livebook instance, 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={~p"/settings"}
add_env_var_path={~p"/settings/env-var/new"}
/>
The configuration in this section changes only your Livebook
experience and is saved in your browser.
Code editor
<.switch_field
name="editor_auto_completion"
label="Show completion list while typing"
value={false}
/>
<.switch_field
name="editor_auto_signature"
label="Show function signature while typing"
value={false}
/>
<.switch_field name="editor_font_size" label="Increase font size" value={false} />
<.switch_field name="editor_light_theme" label="Use light theme" value={false} />
<.switch_field
name="editor_markdown_word_wrap"
label="Wrap words in Markdown"
value={false}
/>
<.modal
:if={@live_action == :add_file_system}
id="add-file-system-modal"
show
width={:medium}
patch={~p"/settings"}
>
<.live_component
module={LivebookWeb.SettingsLive.AddFileSystemComponent}
id="add-file-system"
return_to={~p"/settings"}
/>
<.modal
:if={@live_action in [:add_env_var, :edit_env_var]}
id="env-var-modal"
show
width={:medium}
patch={~p"/settings"}
>
<.live_component
module={LivebookWeb.EnvVarComponent}
id="env-var"
env_var={@env_var}
headline="Configure your application global environment variables."
return_to={~p"/settings"}
/>
"""
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}
target={self()}
>
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,
default_file_system_id: Livebook.Settings.default_file_system_id()
)}
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 = socket.assigns.env_var || %Livebook.Settings.EnvVar{}
case Livebook.Settings.set_env_var(env_var, attrs) do
{:ok, _} ->
{:noreply, push_patch(socket, to: ~p"/settings")}
{:error, _changeset} ->
{:noreply, socket}
end
end
def handle_event("edit_env_var", %{"env_var" => key}, socket) do
{:noreply, push_patch(socket, to: "/settings/env-var/edit/#{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