├── .dockerignore ├── .editorconfig ├── .formatter.exs ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md ├── imgs │ └── welcome.png ├── scripts │ └── app │ │ ├── build_macos.sh │ │ └── build_windows.sh └── workflows │ ├── assets.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── RELEASE.md ├── SECURITY.md ├── assets ├── .babelrc ├── build.js ├── css │ ├── ansi.css │ ├── app.css │ ├── components.css │ ├── js_interop.css │ ├── live_view.css │ ├── markdown.css │ ├── notebook.css │ ├── tooltips.css │ └── utilities.css ├── js │ ├── app.js │ ├── confirm.js │ ├── dom.js │ ├── events.js │ ├── hooks │ │ ├── app_auth.js │ │ ├── audio_input.js │ │ ├── cell.js │ │ ├── cell_editor.js │ │ ├── cell_editor │ │ │ ├── live_editor.js │ │ │ └── live_editor │ │ │ │ ├── codemirror │ │ │ │ ├── collab.js │ │ │ │ ├── collab_markers.js │ │ │ │ ├── commands.js │ │ │ │ ├── doctests.js │ │ │ │ ├── formatter.js │ │ │ │ ├── hover_details.js │ │ │ │ ├── languages.js │ │ │ │ ├── read_only_hint.js │ │ │ │ ├── selecting_class.js │ │ │ │ ├── signature.js │ │ │ │ ├── theme.js │ │ │ │ ├── toggle_with.js │ │ │ │ └── tree_utils.js │ │ │ │ ├── collab_client.js │ │ │ │ ├── connection.js │ │ │ │ └── highlight.js │ │ ├── custom_view_settings.js │ │ ├── dropzone.js │ │ ├── editor_settings.js │ │ ├── emoji_picker.js │ │ ├── focus_on_update.js │ │ ├── headline.js │ │ ├── highlight.js │ │ ├── image_input.js │ │ ├── image_output.js │ │ ├── index.js │ │ ├── js_view.js │ │ ├── js_view │ │ │ ├── channel.js │ │ │ └── iframe.js │ │ ├── keyboard_control.js │ │ ├── markdown_renderer.js │ │ ├── scroll_on_update.js │ │ ├── session.js │ │ ├── session │ │ │ └── cursor_history.js │ │ ├── textarea_autosize.js │ │ ├── timer.js │ │ ├── user_form.js │ │ ├── utc_datetime_input.js │ │ ├── utc_time_input.js │ │ └── virtualized_lines.js │ └── lib │ │ ├── app.js │ │ ├── attribute.js │ │ ├── cache_lru.js │ │ ├── codec.js │ │ ├── delta.js │ │ ├── emitter.js │ │ ├── key_buffer.js │ │ ├── live_store.js │ │ ├── markdown.js │ │ ├── markdown │ │ └── mermaid.js │ │ ├── notebook.js │ │ ├── pubsub.js │ │ ├── settings.js │ │ ├── storage.js │ │ ├── text_utils.js │ │ ├── user.js │ │ └── utils.js ├── package-lock.json ├── package.json ├── tailwind.config.js └── test │ ├── hooks │ ├── cell_editor │ │ └── live_editor │ │ │ └── collab_client.test.js │ └── session │ │ └── cursor_history.test.js │ └── lib │ ├── delta.test.js │ ├── emitter.test.js │ ├── pubsub.test.js │ └── text_utils.test.js ├── config ├── config.exs ├── dev.exs ├── prod.exs ├── runtime.exs └── test.exs ├── docs ├── authentication.md ├── authentication │ ├── basic_auth.md │ ├── cloudflare.md │ ├── custom_auth.md │ ├── google_iap.md │ └── tailscale.md ├── deployment │ ├── clustering.md │ ├── docker.md │ ├── fips.md │ └── nginx_https.md ├── images │ ├── add_app_server_to_deployment_group.png │ ├── add_shared_file_storage.png │ ├── add_shared_secret.png │ ├── add_shared_secret_from_notebook.png │ ├── app_preview.png │ ├── app_server_authentication.png │ ├── app_server_docker.png │ ├── app_server_setup.png │ ├── app_server_setup_message.png │ ├── auth_via_teams.png │ ├── deploy_button.png │ ├── deployed_app.png │ ├── deployed_apps.png │ ├── deployment_group_form.png │ ├── deployment_groups_inside_workspace.png │ ├── email_domain_auth.png │ ├── github_stars_notebook.png │ ├── instructions_setup_app_server.png │ └── oidc_groups_auth.png ├── stamping.md ├── teams │ ├── deploy_app.md │ ├── email_domain.md │ ├── intro_to_teams.md │ ├── oidc_groups.md │ ├── oidc_sso.md │ ├── shared_file_storages.md │ ├── shared_secrets.md │ └── teams_concepts.md └── use_cases.md ├── elixirkit ├── .formatter.exs ├── .gitignore ├── README.md ├── demo │ ├── .formatter.exs │ ├── .gitignore │ ├── README.md │ ├── lib │ │ └── demo.ex │ ├── mix.exs │ ├── rel │ │ ├── appkit │ │ │ ├── .gitignore │ │ │ ├── App.entitlements │ │ │ ├── Package.swift │ │ │ ├── README.md │ │ │ ├── Sources │ │ │ │ └── Demo │ │ │ │ │ └── Demo.swift │ │ │ ├── build_app.sh │ │ │ ├── build_dmg.sh │ │ │ ├── run.sh │ │ │ └── run_app.sh │ │ ├── dotnet │ │ │ ├── .gitignore │ │ │ ├── Demo.cs │ │ │ ├── Demo.csproj │ │ │ ├── README.md │ │ │ └── run.sh │ │ ├── swift │ │ │ ├── .gitignore │ │ │ ├── Package.swift │ │ │ ├── README.md │ │ │ ├── Sources │ │ │ │ └── Demo │ │ │ │ │ └── Demo.swift │ │ │ └── run.sh │ │ └── winforms │ │ │ ├── .gitignore │ │ │ ├── Demo.cs │ │ │ ├── Demo.csproj │ │ │ ├── README.md │ │ │ └── run.sh │ └── test │ │ ├── demo_test.exs │ │ └── test_helper.exs ├── elixirkit_dotnet │ ├── .gitignore │ ├── ElixirKit.cs │ └── ElixirKit.csproj ├── elixirkit_swift │ ├── .gitignore │ ├── Package.swift │ ├── README.md │ ├── Scripts │ │ ├── build_macos_app.sh │ │ └── build_macos_dmg.sh │ ├── Sources │ │ └── ElixirKit │ │ │ └── ElixirKit.swift │ └── Tests │ │ └── ElixirKitTests │ │ └── ElixirKitTests.swift ├── lib │ ├── elixirkit.ex │ └── elixirkit │ │ ├── application.ex │ │ └── server.ex ├── mix.exs ├── otp_bootstrap │ ├── .gitignore │ ├── build.sh │ └── build_macos_universal.sh └── test │ ├── elixirkit_test.exs │ └── test_helper.exs ├── iframe ├── .formatter.exs ├── .gitignore ├── Dockerfile ├── README.md ├── fly.toml ├── lib │ ├── livebook_space │ │ └── application.ex │ └── livebook_space_web │ │ └── plug.ex ├── mix.exs ├── mix.lock └── priv │ └── static │ ├── iframe │ ├── v1.html │ ├── v2.html │ ├── v3.html │ ├── v4.html │ └── v5.html │ ├── images │ ├── favicon.svg │ └── logo.png │ ├── index.html │ └── robots.txt ├── lib ├── livebook.ex ├── livebook │ ├── app.ex │ ├── application.ex │ ├── apps.ex │ ├── apps │ │ ├── app_spec.ex │ │ ├── deployer.ex │ │ ├── deployment_supervisor.ex │ │ ├── manager.ex │ │ ├── manager_watcher.ex │ │ ├── path_app_spec.ex │ │ ├── preview_app_spec.ex │ │ └── teams_app_spec.ex │ ├── config.ex │ ├── ecto_types │ │ └── hex_color.ex │ ├── epmd.ex │ ├── epmd │ │ └── node_pool.ex │ ├── file_system.ex │ ├── file_system │ │ ├── file.ex │ │ ├── local.ex │ │ ├── s3.ex │ │ ├── s3 │ │ │ ├── client.ex │ │ │ └── xml.ex │ │ └── utils.ex │ ├── file_systems.ex │ ├── fly_api.ex │ ├── hubs.ex │ ├── hubs │ │ ├── broadcasts.ex │ │ ├── dockerfile.ex │ │ ├── metadata.ex │ │ ├── personal.ex │ │ ├── provider.ex │ │ ├── team.ex │ │ └── team_client.ex │ ├── intellisense.ex │ ├── intellisense │ │ ├── docs.ex │ │ ├── identifier_matcher.ex │ │ └── signature_matcher.ex │ ├── k8s │ │ ├── pod.ex │ │ └── pvc.ex │ ├── k8s_api.ex │ ├── live_markdown.ex │ ├── live_markdown │ │ ├── export.ex │ │ ├── import.ex │ │ └── markdown_helpers.ex │ ├── migration.ex │ ├── notebook.ex │ ├── notebook │ │ ├── app_settings.ex │ │ ├── cell.ex │ │ ├── cell │ │ │ ├── code.ex │ │ │ ├── markdown.ex │ │ │ └── smart.ex │ │ ├── content_loader.ex │ │ ├── export │ │ │ └── elixir.ex │ │ ├── learn.ex │ │ ├── learn │ │ │ ├── chat_app.livemd │ │ │ ├── distributed_portals_with_elixir.livemd │ │ │ ├── files │ │ │ │ ├── portal-drop.jpeg │ │ │ │ └── portal-list.jpeg │ │ │ ├── github_stars.livemd │ │ │ ├── intro_to_explorer.livemd │ │ │ ├── intro_to_livebook.livemd │ │ │ ├── intro_to_maplibre.livemd │ │ │ ├── intro_to_vega_lite.livemd │ │ │ └── kino │ │ │ │ ├── custom_kinos.livemd │ │ │ │ ├── intro_to_kino.livemd │ │ │ │ ├── smart_cells.livemd │ │ │ │ └── vm_introspection.livemd │ │ └── section.ex │ ├── notebook_manager.ex │ ├── proxy │ │ ├── adapter.ex │ │ ├── handler.ex │ │ └── server.ex │ ├── release.ex │ ├── runtime.ex │ ├── runtime │ │ ├── attached.ex │ │ ├── definitions.ex │ │ ├── dependencies.ex │ │ ├── embedded.ex │ │ ├── epmd.ex │ │ ├── erl_dist.ex │ │ ├── erl_dist │ │ │ ├── evaluator_supervisor.ex │ │ │ ├── io_forward_gl.ex │ │ │ ├── logger_gl_handler.ex │ │ │ ├── node_manager.ex │ │ │ ├── runtime_server.ex │ │ │ └── smart_cell_gl.ex │ │ ├── evaluator.ex │ │ ├── evaluator │ │ │ ├── client_tracker.ex │ │ │ ├── doctests.ex │ │ │ ├── formatter.ex │ │ │ ├── io_proxy.ex │ │ │ ├── object_tracker.ex │ │ │ └── tracer.ex │ │ ├── fly.ex │ │ ├── k8s.ex │ │ ├── remote_utils.ex │ │ └── standalone.ex │ ├── secrets.ex │ ├── secrets │ │ └── secret.ex │ ├── session.ex │ ├── session │ │ ├── data.ex │ │ ├── file_guard.ex │ │ └── worker.ex │ ├── sessions.ex │ ├── settings.ex │ ├── settings │ │ └── env_var.ex │ ├── stamping.ex │ ├── storage.ex │ ├── system_resources.ex │ ├── teams.ex │ ├── teams │ │ ├── agent.ex │ │ ├── agent_key.ex │ │ ├── app_deployment.ex │ │ ├── authorization_group.ex │ │ ├── broadcasts.ex │ │ ├── connection.ex │ │ ├── deployment_group.ex │ │ ├── environment_variable.ex │ │ ├── org.ex │ │ ├── requests.ex │ │ └── web_socket.ex │ ├── text │ │ ├── delta.ex │ │ ├── delta │ │ │ ├── operation.ex │ │ │ └── transformation.ex │ │ ├── js.ex │ │ └── selection.ex │ ├── tracker.ex │ ├── update_check.ex │ ├── users.ex │ ├── users │ │ └── user.ex │ ├── utils.ex │ ├── utils │ │ ├── ansi.ex │ │ ├── graph.ex │ │ ├── supervision_step.ex │ │ ├── time.ex │ │ └── unique_task.ex │ ├── zta.ex │ └── zta │ │ ├── basic_auth.ex │ │ ├── cloudflare.ex │ │ ├── google_iap.ex │ │ ├── livebook_teams.ex │ │ ├── pass_through.ex │ │ └── tailscale.ex ├── livebook_app.ex ├── livebook_cli.ex ├── livebook_cli │ ├── server.ex │ └── task.ex ├── livebook_web.ex ├── livebook_web │ ├── channels │ │ ├── js_view_channel.ex │ │ └── socket.ex │ ├── components │ │ ├── app_components.ex │ │ ├── confirm.ex │ │ ├── core_components.ex │ │ ├── file_system_components.ex │ │ ├── form_components.ex │ │ ├── layout_components.ex │ │ ├── layouts.ex │ │ ├── layouts │ │ │ ├── app.html.heex │ │ │ ├── live.html.heex │ │ │ └── root.html.heex │ │ ├── notebook_components.ex │ │ └── user_components.ex │ ├── controllers │ │ ├── auth_controller.ex │ │ ├── auth_html.ex │ │ ├── auth_html │ │ │ └── index.html.heex │ │ ├── error_html.ex │ │ ├── error_json.ex │ │ ├── health_controller.ex │ │ ├── session_controller.ex │ │ ├── user_controller.ex │ │ └── user_html.ex │ ├── endpoint.ex │ ├── errors.ex │ ├── helpers │ │ ├── ansi_helpers.ex │ │ ├── codec_helpers.ex │ │ ├── html_helpers.ex │ │ └── session_helpers.ex │ ├── iframe_endpoint.ex │ ├── live │ │ ├── app_auth_live.ex │ │ ├── app_live.ex │ │ ├── app_session_live.ex │ │ ├── app_session_live │ │ │ ├── cell_outputs.ex │ │ │ └── source_component.ex │ │ ├── apps_dashboard_live.ex │ │ ├── apps_live.ex │ │ ├── file_select_component.ex │ │ ├── file_system_writer.ex │ │ ├── home_live.ex │ │ ├── home_live │ │ │ └── session_list_component.ex │ │ ├── hooks │ │ │ ├── app_auth_hook.ex │ │ │ ├── auth_hook.ex │ │ │ ├── sidebar_hook.ex │ │ │ └── user_hook.ex │ │ ├── hub │ │ │ ├── edit │ │ │ │ ├── personal_component.ex │ │ │ │ └── team_component.ex │ │ │ ├── edit_live.ex │ │ │ ├── file_system_form_component.ex │ │ │ ├── file_system_list_component.ex │ │ │ ├── new_live.ex │ │ │ ├── secret_form_component.ex │ │ │ ├── secret_list_component.ex │ │ │ └── teams │ │ │ │ ├── deployment_group_agent_component.ex │ │ │ │ ├── deployment_group_component.ex │ │ │ │ └── deployment_group_form_component.ex │ │ ├── js_view_component.ex │ │ ├── learn_live.ex │ │ ├── notebook_cards_component.ex │ │ ├── open_live.ex │ │ ├── open_live │ │ │ ├── file_component.ex │ │ │ ├── source_component.ex │ │ │ ├── upload_component.ex │ │ │ └── url_component.ex │ │ ├── output.ex │ │ ├── output │ │ │ ├── audio_input_component.ex │ │ │ ├── control_component.ex │ │ │ ├── control_form_component.ex │ │ │ ├── file_input_component.ex │ │ │ ├── frame_component.ex │ │ │ ├── grid_component.ex │ │ │ ├── image_component.ex │ │ │ ├── image_input_component.ex │ │ │ ├── input_component.ex │ │ │ ├── markdown_component.ex │ │ │ ├── plain_text_component.ex │ │ │ ├── tabs_component.ex │ │ │ └── terminal_text_component.ex │ │ ├── session_live.ex │ │ ├── session_live │ │ │ ├── add_file_entry_file_component.ex │ │ │ ├── add_file_entry_unlisted_component.ex │ │ │ ├── add_file_entry_upload_component.ex │ │ │ ├── add_file_entry_url_component.ex │ │ │ ├── app_docker_component.ex │ │ │ ├── app_info_component.ex │ │ │ ├── app_settings_component.ex │ │ │ ├── app_teams_live.ex │ │ │ ├── attached_runtime_component.ex │ │ │ ├── bin_component.ex │ │ │ ├── cell_component.ex │ │ │ ├── code_cell_settings_component.ex │ │ │ ├── custom_view_component.ex │ │ │ ├── embedded_runtime_component.ex │ │ │ ├── export_component.ex │ │ │ ├── export_elixir_component.ex │ │ │ ├── export_live_markdown_component.ex │ │ │ ├── files_list_component.ex │ │ │ ├── fly_runtime_component.ex │ │ │ ├── insert_buttons_component.ex │ │ │ ├── insert_file_component.ex │ │ │ ├── insert_image_component.ex │ │ │ ├── k8s_runtime_component.ex │ │ │ ├── package_search_live.ex │ │ │ ├── persistence_component.ex │ │ │ ├── rename_file_entry_component.ex │ │ │ ├── render.ex │ │ │ ├── runtime_component.ex │ │ │ ├── save_runtime_config_component.ex │ │ │ ├── secrets_component.ex │ │ │ ├── secrets_list_component.ex │ │ │ ├── section_component.ex │ │ │ ├── shortcuts_component.ex │ │ │ └── standalone_runtime_component.ex │ │ ├── settings_live.ex │ │ ├── settings_live │ │ │ ├── env_var_component.ex │ │ │ └── env_vars_component.ex │ │ └── user_component.ex │ ├── plugs │ │ ├── auth_plug.ex │ │ ├── configured_plug.ex │ │ ├── proxy_plug.ex │ │ └── user_plug.ex │ ├── router.ex │ ├── telemetry.ex │ └── verified_routes.ex └── mix │ └── tasks │ └── compile.livebook_priv.ex ├── mix.exs ├── mix.lock ├── proto ├── .formatter.exs ├── .gitignore ├── README.md ├── lib │ ├── livebook_proto.ex │ └── livebook_proto │ │ ├── agent.pb.ex │ │ ├── agent_connected.pb.ex │ │ ├── agent_joined.pb.ex │ │ ├── agent_key.pb.ex │ │ ├── agent_left.pb.ex │ │ ├── app_deployment.pb.ex │ │ ├── app_deployment_started.pb.ex │ │ ├── app_deployment_status.pb.ex │ │ ├── app_deployment_status_report.pb.ex │ │ ├── app_deployment_status_type.pb.ex │ │ ├── app_deployment_stopped.pb.ex │ │ ├── app_deployment_updated.pb.ex │ │ ├── authorization_group.pb.ex │ │ ├── billing_status.pb.ex │ │ ├── billing_status_canceled.pb.ex │ │ ├── billing_status_canceling.pb.ex │ │ ├── billing_status_trial_ended.pb.ex │ │ ├── billing_status_trialing.pb.ex │ │ ├── deployment_group.pb.ex │ │ ├── deployment_group_created.pb.ex │ │ ├── deployment_group_deleted.pb.ex │ │ ├── deployment_group_secret.pb.ex │ │ ├── deployment_group_updated.pb.ex │ │ ├── environment_variable.pb.ex │ │ ├── error.pb.ex │ │ ├── event.pb.ex │ │ ├── file_system.pb.ex │ │ ├── file_system_created.pb.ex │ │ ├── file_system_deleted.pb.ex │ │ ├── file_system_updated.pb.ex │ │ ├── org_status_cancel.pb.ex │ │ ├── org_status_trial.pb.ex │ │ ├── org_updated.pb.ex │ │ ├── secret.pb.ex │ │ ├── secret_created.pb.ex │ │ ├── secret_deleted.pb.ex │ │ ├── secret_updated.pb.ex │ │ ├── user_connected.pb.ex │ │ └── user_deleted.pb.ex ├── messages.proto ├── mix.exs └── mix.lock ├── rel ├── app │ ├── env.bat.eex │ ├── env.sh.eex │ ├── macos │ │ ├── .gitignore │ │ ├── App.entitlements │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── AppIcon-128px.png │ │ │ │ ├── AppIcon-16px.png │ │ │ │ ├── AppIcon-256px-1.png │ │ │ │ ├── AppIcon-256px.png │ │ │ │ ├── AppIcon-32px-1.png │ │ │ │ ├── AppIcon-32px.png │ │ │ │ ├── AppIcon-512px-1.png │ │ │ │ ├── AppIcon-512px.png │ │ │ │ ├── AppIcon-64px.png │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ └── Icon.iconset │ │ │ │ ├── icon_128x128.png │ │ │ │ ├── icon_128x128@2x.png │ │ │ │ ├── icon_16x16.png │ │ │ │ ├── icon_16x16@2x.png │ │ │ │ ├── icon_256x256.png │ │ │ │ ├── icon_256x256@2x.png │ │ │ │ ├── icon_32x32.png │ │ │ │ ├── icon_32x32@2x.png │ │ │ │ ├── icon_512x512.png │ │ │ │ └── icon_512x512@2x.png │ │ ├── Info.plist │ │ ├── Package.swift │ │ ├── README.md │ │ ├── Sources │ │ │ └── Livebook │ │ │ │ └── Livebook.swift │ │ ├── build_app.sh │ │ ├── build_dmg.sh │ │ └── run.sh │ ├── standalone.exs │ ├── vm.args.eex │ └── windows │ │ ├── .gitignore │ │ ├── App.manifest │ │ ├── Installer.nsi │ │ ├── Livebook.cs │ │ ├── Livebook.csproj │ │ ├── README.md │ │ ├── Resources │ │ └── AppIcon.ico │ │ ├── build.sh │ │ ├── build_installer.sh │ │ ├── env.sh │ │ └── run.sh └── server │ ├── env.bat.eex │ ├── env.sh.eex │ ├── overlays │ ├── bin │ │ ├── server │ │ ├── start_flame.exs │ │ ├── start_runtime.exs │ │ ├── warmup_apps │ │ └── warmup_apps.bat │ └── user │ │ └── extensions │ │ └── .gitkeep │ ├── remote.vm.args.eex │ └── vm.args.eex ├── static ├── assets │ ├── KaTeX_AMS-Regular-CYEKBG2K.woff │ ├── KaTeX_AMS-Regular-JKX5W2C4.ttf │ ├── KaTeX_AMS-Regular-U6PRYMIZ.woff2 │ ├── KaTeX_Caligraphic-Bold-5QL5CMTE.woff2 │ ├── KaTeX_Caligraphic-Bold-WZ3QSGD3.woff │ ├── KaTeX_Caligraphic-Bold-ZTS3R3HK.ttf │ ├── KaTeX_Caligraphic-Regular-3LKEU76G.woff │ ├── KaTeX_Caligraphic-Regular-A7XRTZ5Q.ttf │ ├── KaTeX_Caligraphic-Regular-KX5MEWCF.woff2 │ ├── KaTeX_Fraktur-Bold-2QVFK6NQ.woff2 │ ├── KaTeX_Fraktur-Bold-T4SWXBMT.woff │ ├── KaTeX_Fraktur-Bold-WGHVTYOR.ttf │ ├── KaTeX_Fraktur-Regular-2PEIFJSJ.woff2 │ ├── KaTeX_Fraktur-Regular-5U4OPH2X.ttf │ ├── KaTeX_Fraktur-Regular-PQMHCIK6.woff │ ├── KaTeX_Main-Bold-2GA4IZIN.woff │ ├── KaTeX_Main-Bold-W5FBVCZM.ttf │ ├── KaTeX_Main-Bold-YP5VVQRP.woff2 │ ├── KaTeX_Main-BoldItalic-4P4C7HJH.woff │ ├── KaTeX_Main-BoldItalic-N4V3DX7S.woff2 │ ├── KaTeX_Main-BoldItalic-ODMLBJJQ.ttf │ ├── KaTeX_Main-Italic-I43T2HSR.ttf │ ├── KaTeX_Main-Italic-RELBIK7M.woff2 │ ├── KaTeX_Main-Italic-SASNQFN2.woff │ ├── KaTeX_Main-Regular-ARRPAO67.woff2 │ ├── KaTeX_Main-Regular-P5I74A2A.woff │ ├── KaTeX_Main-Regular-W74P5G27.ttf │ ├── KaTeX_Math-BoldItalic-6EBV3DK5.woff │ ├── KaTeX_Math-BoldItalic-K4WTGH3J.woff2 │ ├── KaTeX_Math-BoldItalic-VB447A4D.ttf │ ├── KaTeX_Math-Italic-6KGCHLFN.woff2 │ ├── KaTeX_Math-Italic-KKK3USB2.woff │ ├── KaTeX_Math-Italic-SON4MRCA.ttf │ ├── KaTeX_SansSerif-Bold-RRNVJFFW.woff2 │ ├── KaTeX_SansSerif-Bold-STQ6RXC7.ttf │ ├── KaTeX_SansSerif-Bold-X5M5EMOD.woff │ ├── KaTeX_SansSerif-Italic-HMPFTM52.woff2 │ ├── KaTeX_SansSerif-Italic-PSN4QKYX.woff │ ├── KaTeX_SansSerif-Italic-WTBAZBGY.ttf │ ├── KaTeX_SansSerif-Regular-2TL3USAE.ttf │ ├── KaTeX_SansSerif-Regular-OQCII6EP.woff │ ├── KaTeX_SansSerif-Regular-XIQ62X4E.woff2 │ ├── KaTeX_Script-Regular-72OLXYNA.ttf │ ├── KaTeX_Script-Regular-A5IFOEBS.woff │ ├── KaTeX_Script-Regular-APUWIHLP.woff2 │ ├── KaTeX_Size1-Regular-4HRHTS65.woff │ ├── KaTeX_Size1-Regular-5LRUTBFT.woff2 │ ├── KaTeX_Size1-Regular-7K6AASVL.ttf │ ├── KaTeX_Size2-Regular-222HN3GT.ttf │ ├── KaTeX_Size2-Regular-K5ZHAIS6.woff │ ├── KaTeX_Size2-Regular-LELKET5D.woff2 │ ├── KaTeX_Size3-Regular-TLFPAHDE.woff │ ├── KaTeX_Size3-Regular-UFCO6WCA.ttf │ ├── KaTeX_Size3-Regular-WQRQ47UD.woff2 │ ├── KaTeX_Size4-Regular-7PGNVPQK.ttf │ ├── KaTeX_Size4-Regular-CDMV7U5C.woff2 │ ├── KaTeX_Size4-Regular-PKMWZHNC.woff │ ├── KaTeX_Typewriter-Regular-3F5K6SQ6.ttf │ ├── KaTeX_Typewriter-Regular-MJMFSK64.woff │ ├── KaTeX_Typewriter-Regular-VBYJ4NRC.woff2 │ ├── app.css │ ├── app.js │ ├── architecture-I3QFYML2-6IR3LVRE.js │ ├── architectureDiagram-UYN6MBPD-B3JL7HDC.js │ ├── blockDiagram-ZHA2E4KO-PLGM37UV.js │ ├── c4Diagram-6F5ED5ID-5HDWF5MA.js │ ├── chunk-24JW6VB3.js │ ├── chunk-47P5NBBB.js │ ├── chunk-5JLOMFCN.js │ ├── chunk-5XJO6U7A.js │ ├── chunk-6IOWLTMD.js │ ├── chunk-6KI6TUY3.js │ ├── chunk-BQJTSJOB.js │ ├── chunk-E4RBDSW5.js │ ├── chunk-FIA6NTGC.js │ ├── chunk-GJ6CDUB5.js │ ├── chunk-HTMW6R2V.js │ ├── chunk-IDH2HD35.js │ ├── chunk-IJWB56EA.js │ ├── chunk-JVEBZTDG.js │ ├── chunk-KRX7QNR4.js │ ├── chunk-L3A5WJI3.js │ ├── chunk-LGVNNTMI.js │ ├── chunk-MF5TUIBL.js │ ├── chunk-MGYUK2XN.js │ ├── chunk-NCYNIMJ4.js │ ├── chunk-NKCQAART.js │ ├── chunk-RVGDP346.js │ ├── chunk-SA2EGKJT.js │ ├── chunk-SISR4MA5.js │ ├── chunk-TZMIYTTY.js │ ├── chunk-UY4K4Z6W.js │ ├── chunk-VJZZWRCC.js │ ├── chunk-VW6A6SAT.js │ ├── chunk-W7LTUAGZ.js │ ├── chunk-WNVR7S66.js │ ├── chunk-WYMAA4MH.js │ ├── chunk-XHGORZV2.js │ ├── chunk-ZP5QXAVO.js │ ├── chunk-ZPWCXWQI.js │ ├── classDiagram-LNE6IOMH-QE5QBIDP.js │ ├── classDiagram-v2-MQ7JQ4JX-6A4BS74I.js │ ├── dagre-4EVJKHTY-TI3ZPXZV.js │ ├── diagram-QW4FP2JN-W3LGLVR5.js │ ├── erDiagram-6RL3IURR-TILZZ7DE.js │ ├── flowDiagram-7ASYPVHJ-6FSFQFKV.js │ ├── ganttDiagram-NTVNEXSI-I375UW4Z.js │ ├── gitGraph-YCYPL57B-J4JOQTHP.js │ ├── gitGraphDiagram-NRZ2UAAF-LIITHXBO.js │ ├── info-46DW6VJ7-VCNA5FQQ.js │ ├── infoDiagram-A4XQUW5V-2UH4OEMN.js │ ├── inter-cyrillic-400-normal-JTDOFDEL.woff │ ├── inter-cyrillic-400-normal-KR3WP37A.woff2 │ ├── inter-cyrillic-500-normal-L3ERAO2A.woff2 │ ├── inter-cyrillic-500-normal-YGY63224.woff │ ├── inter-cyrillic-600-normal-RRU6DOYW.woff2 │ ├── inter-cyrillic-600-normal-YAIFIQFF.woff │ ├── inter-cyrillic-ext-400-normal-ERANL6CF.woff2 │ ├── inter-cyrillic-ext-400-normal-NYJ3VVCY.woff │ ├── inter-cyrillic-ext-500-normal-3D2TL7BV.woff2 │ ├── inter-cyrillic-ext-500-normal-MVWVUDWY.woff │ ├── inter-cyrillic-ext-600-normal-3WK3AIAE.woff2 │ ├── inter-cyrillic-ext-600-normal-ML7GDJ53.woff │ ├── inter-greek-400-normal-22W3W6EP.woff │ ├── inter-greek-400-normal-K452ZUNI.woff2 │ ├── inter-greek-500-normal-SR6UVMZM.woff2 │ ├── inter-greek-500-normal-V5MWXBXZ.woff │ ├── inter-greek-600-normal-52AI77RO.woff2 │ ├── inter-greek-600-normal-MHDVNT3B.woff │ ├── inter-greek-ext-400-normal-6GLOS2AC.woff │ ├── inter-greek-ext-400-normal-7CMSSAC3.woff2 │ ├── inter-greek-ext-500-normal-GTVYGHNV.woff │ ├── inter-greek-ext-500-normal-L7SAHJMO.woff2 │ ├── inter-greek-ext-600-normal-2BPZID6V.woff2 │ ├── inter-greek-ext-600-normal-URVVZOUP.woff │ ├── inter-latin-400-normal-GYFJJKJF.woff2 │ ├── inter-latin-400-normal-HCV22YHT.woff │ ├── inter-latin-500-normal-5GHDSDAY.woff2 │ ├── inter-latin-500-normal-PGDKKSLY.woff │ ├── inter-latin-600-normal-56WZKGNJ.woff2 │ ├── inter-latin-600-normal-RLL3LKKR.woff │ ├── inter-latin-ext-400-normal-2H3KLIXK.woff │ ├── inter-latin-ext-400-normal-OMTSHYQS.woff2 │ ├── inter-latin-ext-500-normal-R6CV3WNH.woff2 │ ├── inter-latin-ext-500-normal-WO7CCPI4.woff │ ├── inter-latin-ext-600-normal-IPOBZ3TF.woff2 │ ├── inter-latin-ext-600-normal-Q5LOCDKP.woff │ ├── inter-vietnamese-400-normal-3ZH4IT4J.woff2 │ ├── inter-vietnamese-400-normal-CQIZECWR.woff │ ├── inter-vietnamese-500-normal-PQFGSX3P.woff2 │ ├── inter-vietnamese-500-normal-YWCA5IC5.woff │ ├── inter-vietnamese-600-normal-RMFHJUBF.woff │ ├── inter-vietnamese-600-normal-TVIYLGI7.woff2 │ ├── jetbrains-mono-cyrillic-400-normal-C5JECUCT.woff2 │ ├── jetbrains-mono-cyrillic-400-normal-ZHDMAAUB.woff │ ├── jetbrains-mono-cyrillic-ext-400-normal-C5DDHB7N.woff │ ├── jetbrains-mono-cyrillic-ext-400-normal-C7IFWGF6.woff2 │ ├── jetbrains-mono-greek-400-normal-KF6EKC2J.woff │ ├── jetbrains-mono-greek-400-normal-O7JTTR3P.woff2 │ ├── jetbrains-mono-latin-400-normal-3OOWLGQ2.woff2 │ ├── jetbrains-mono-latin-400-normal-4JODGGIE.woff │ ├── jetbrains-mono-latin-ext-400-normal-3RACNWYK.woff │ ├── jetbrains-mono-latin-ext-400-normal-JVR3IR4Z.woff2 │ ├── jetbrains-mono-vietnamese-400-normal-7A56GUVN.woff │ ├── jetbrains-mono-vietnamese-400-normal-KU7YLUPA.woff2 │ ├── journeyDiagram-G5WM74LC-IOGZ4ER4.js │ ├── kanban-definition-QRCXZQQD-665S4FCL.js │ ├── katex-R4V72ZOM.js │ ├── mermaid.core-PK4ATILI.js │ ├── mindmap-definition-GWI6TPTV-TXRXVVPF.js │ ├── packet-W2GHVCYJ-FC4ERNLH.js │ ├── pie-BEWT4RHE-5KCD7UAH.js │ ├── pieDiagram-YF2LJOPJ-F5SQDVTC.js │ ├── quadrantDiagram-OS5C2QUG-JOFAEUBY.js │ ├── red-hat-text-latin-400-normal-OV4KODUR.woff2 │ ├── red-hat-text-latin-400-normal-YWAZCVDO.woff │ ├── red-hat-text-latin-ext-400-normal-H2UN6E6K.woff │ ├── red-hat-text-latin-ext-400-normal-PD2NZNLC.woff2 │ ├── remixicon-6QPK2BSM.ttf │ ├── remixicon-7463F4HZ.woff2 │ ├── remixicon-DH65ADK5.woff │ ├── remixicon-OJBB7CW7.eot │ ├── remixicon-TZP22IVM.svg │ ├── requirementDiagram-MIRIMTAZ-3ZJXLEGT.js │ ├── sankeyDiagram-Y46BX6SQ-E54YZX3S.js │ ├── sequenceDiagram-G6AWOVSC-YPQCR6DD.js │ ├── stateDiagram-MAYHULR4-5MBSR5JM.js │ ├── stateDiagram-v2-4JROLMXI-VMRUNBYI.js │ ├── timeline-definition-U7ZMHBDA-JF4H43ED.js │ └── xychartDiagram-6QU3TZC5-6SD7MGQD.js ├── favicons │ ├── favicon-errored.svg │ ├── favicon-evaluating.svg │ ├── favicon-stale.svg │ ├── favicon.png │ └── favicon.svg ├── images │ ├── elixir-portal.jpeg │ ├── elixir.png │ ├── explorer.png │ ├── fly.svg │ ├── github-stars.png │ ├── kino.png │ ├── learn-deploy.svg │ ├── logo-with-text.png │ ├── logo.png │ ├── maplibre.png │ ├── teams.png │ └── vega_lite.png └── robots.txt ├── test ├── README.md ├── livebook │ ├── app_test.exs │ ├── apps │ │ ├── deployer_test.exs │ │ ├── manager_test.exs │ │ └── path_app_spec_test.exs │ ├── apps_test.exs │ ├── config_test.exs │ ├── ecto_types │ │ └── hex_color_test.exs │ ├── epmd │ │ └── node_pool_test.exs │ ├── epmd_test.exs │ ├── file_system │ │ ├── file_test.exs │ │ ├── local_test.exs │ │ └── s3_test.exs │ ├── hubs │ │ ├── dockerfile_test.exs │ │ ├── personal_test.exs │ │ └── provider_test.exs │ ├── intellisense_test.exs │ ├── learn_test.exs │ ├── live_markdown │ │ ├── export_test.exs │ │ ├── import_test.exs │ │ └── markdown_helpers_test.exs │ ├── notebook │ │ ├── content_loader_test.exs │ │ └── export │ │ │ └── elixir_test.exs │ ├── notebook_test.exs │ ├── runtime │ │ ├── attached_test.exs │ │ ├── dependencies_test.exs │ │ ├── erl_dist │ │ │ ├── io_forward_gl_test.exs │ │ │ ├── node_manager_test.exs │ │ │ ├── runtime_server_test.exs │ │ │ └── smart_cell_gl_test.exs │ │ ├── evaluator │ │ │ ├── client_tracker_test.exs │ │ │ ├── io_proxy_test.exs │ │ │ └── object_tracker_test.exs │ │ ├── evaluator_test.exs │ │ ├── fly_test.exs │ │ ├── k8s_test.exs │ │ └── standalone_test.exs │ ├── secrets_test.exs │ ├── session │ │ ├── data_test.exs │ │ └── file_guard_test.exs │ ├── session_test.exs │ ├── sessions_test.exs │ ├── settings_test.exs │ ├── storage_test.exs │ ├── text │ │ ├── delta │ │ │ └── transformation_test.exs │ │ ├── delta_test.exs │ │ └── js_test.exs │ ├── users │ │ └── user_test.exs │ ├── users_test.exs │ ├── utils │ │ ├── ansi_test.exs │ │ ├── time_test.exs │ │ └── unique_task_test.exs │ ├── utils_test.exs │ └── zta │ │ ├── basic_auth_test.exs │ │ ├── cloudflare_test.exs │ │ ├── google_iap_test.exs │ │ └── tailscale_test.exs ├── livebook_teams │ ├── apps_test.exs │ ├── hubs │ │ └── team_client_test.exs │ ├── hubs_test.exs │ ├── teams_test.exs │ ├── web │ │ ├── admin_live_test.exs │ │ ├── app_session_live_test.exs │ │ ├── apps_live_test.exs │ │ ├── hub │ │ │ ├── deployment_group_test.exs │ │ │ ├── edit_live_test.exs │ │ │ └── new_live_test.exs │ │ └── session_live_test.exs │ └── zta │ │ └── livebook_teams_test.exs ├── livebook_test.exs ├── livebook_web │ ├── channels │ │ └── js_view_channel_test.exs │ ├── controllers │ │ ├── endpoint_test.exs │ │ ├── errors_test.exs │ │ ├── session_controller_test.exs │ │ └── user_controller_test.exs │ ├── helpers │ │ ├── ansi_helpers_test.exs │ │ └── html_helpers_test.exs │ ├── live │ │ ├── app_auth_live_test.exs │ │ ├── app_live_test.exs │ │ ├── app_session_live_test.exs │ │ ├── apps_dashboard_live_test.exs │ │ ├── file_select_component_test.exs │ │ ├── home_live_test.exs │ │ ├── hub │ │ │ └── edit_live_test.exs │ │ ├── learn_live_test.exs │ │ ├── open_live_test.exs │ │ ├── output │ │ │ └── plain_text_component_test.exs │ │ ├── session_live_test.exs │ │ └── settings_live_test.exs │ └── plugs │ │ ├── auth_plug_test.exs │ │ ├── proxy_plug_test.exs │ │ └── user_plug_test.exs ├── support │ ├── app_helpers.ex │ ├── assets.tar.gz │ ├── channel_case.ex │ ├── conn_case.ex │ ├── data_case.ex │ ├── factory.ex │ ├── hub_helpers.ex │ ├── integration │ │ ├── teams_rpc.ex │ │ ├── teams_server.ex │ │ └── teams_tests.ex │ ├── k8s_cluster_stub.ex │ ├── noop_runtime.ex │ ├── notebook_app_spec.ex │ ├── notebooks │ │ ├── basic.livemd │ │ └── with_two_sections.livemd │ ├── session_helpers.ex │ ├── static │ │ ├── icon.ico │ │ └── js │ │ │ └── app.js │ ├── teams_integration_case.ex │ ├── test_helpers.ex │ └── test_modules │ │ ├── bad_inspect.ex │ │ └── hidden.ex └── test_helper.exs └── versions /.dockerignore: -------------------------------------------------------------------------------- 1 | # Mirrors .gitignore 2 | 3 | /_build/ 4 | /cover/ 5 | /deps/ 6 | /doc/ 7 | /.fetch 8 | erl_crash.dump 9 | *.ez 10 | livebook-*.tar 11 | npm-debug.log 12 | /assets/node_modules/ 13 | /tmp/ 14 | /livebook 15 | /priv/static 16 | /priv/iframe_static 17 | # Ignore app release files, including build artifacts 18 | /rel/app 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://EditorConfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | -------------------------------------------------------------------------------- /.formatter.exs: -------------------------------------------------------------------------------- 1 | [ 2 | import_deps: [:phoenix, :ecto], 3 | plugins: [Phoenix.LiveView.HTMLFormatter], 4 | inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "rel/*/overlays/**/*.exs"] 5 | ] 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # convert all CRLF to LF 2 | * text=auto eol=lf 3 | 4 | # ensure binary files are not marked as text 5 | *.{ico,png,jpg,jpeg,gif,webp,woff,woff2} binary 6 | 7 | iframe/priv/static/iframe/*.html text eol=lf 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Environment 4 | 5 | 6 | 7 | * Elixir & Erlang/OTP versions (elixir --version): 8 | * Operating system: 9 | * How have you started Livebook (mix phx.server, livebook CLI, Docker, etc): 10 | * Livebook version (use `git rev-parse HEAD` if running with mix): 11 | * Browsers that reproduce this bug (the more the merrier): 12 | * Include what is logged in the browser console: 13 | * Include what is logged to the server console: 14 | 15 | ## Current behavior 16 | 17 | Include detailed steps to reproduce error, errors and stacktraces if appropriate. 18 | 19 | ## Expected behavior 20 | 21 | A short description on how you expect the code to behave. 22 | -------------------------------------------------------------------------------- /.github/imgs/welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/.github/imgs/welcome.png -------------------------------------------------------------------------------- /.github/scripts/app/build_windows.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Usage: 4 | # 5 | # $ sh .github/scripts/app/build_windows.sh 6 | # $ rel/app/windows/bin/LivebookInstall.exe 7 | # $ start livebook://github.com/livebook-dev/livebook/blob/main/test/support/notebooks/basic.livemd 8 | # $ start ./test/support/notebooks/basic.livemd 9 | # 10 | # Note: This script builds the Windows installer. If you just want to test the Windows app locally, run: 11 | # 12 | # $ cd rel/app/windows && ./run.sh 13 | # 14 | # See rel/app/windows/README.md for more information. 15 | set -e 16 | 17 | mix local.hex --force --if-missing 18 | mix local.rebar --force --if-missing 19 | 20 | export MIX_ENV=prod 21 | export MIX_TARGET=app 22 | export ELIXIRKIT_CONFIGURATION=Release 23 | 24 | mix deps.get --only prod 25 | 26 | cd rel/app/windows 27 | ./build_installer.sh 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where 3rd-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | livebook-*.tar 24 | 25 | # If NPM crashes, it generates a log, let's ignore it too. 26 | npm-debug.log 27 | 28 | # The directory NPM downloads your dependencies sources to. 29 | /assets/node_modules/ 30 | 31 | # The directory used by ExUnit :tmp_dir 32 | /tmp/ 33 | 34 | # The built Escript 35 | /livebook 36 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog for Livebook v0.17 2 | 3 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). 4 | 5 | ## [Unreleased](https://github.com/livebook-dev/livebook/tree/main) 6 | 7 | ## v0.16 8 | 9 | The CHANGELOG for v0.16 releases can be found in the [v0.16](https://github.com/livebook-dev/livebook/tree/v0.16/CHANGELOG.md) branch. 10 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Releasing Livebook 2 | 3 | 0. If applicable, release Kino first and update built-in notebooks to reference 4 | the new version. 5 | 1. Make sure the latest CI "Assets" workflow finished. 6 | 2. Switch to (or create) vx.y branch. 7 | 3. If applicable cherry-pick the relevant commits from main onto the vx.y branch. 8 | 1. If you do that, push, wait for CI "Assets" workflow to finish, and pull. 9 | 3. Update version in `mix.exs` and finish changelog. 10 | 4. Run `mix hex.build` as a sanity check. 11 | 5. `git tag vx.y.z`, `git push --tags` 12 | 1. Wait for CI to finish (Docker and Desktop) 13 | 6. Run `mix hex.publish`. 14 | 7. Publish GH release with copied changelog notes. 15 | 8. If you created a branch in step 2., update main changelog to point to 16 | the branch and bump version in mix.exs (with `-dev` suffix). 17 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Currently we support security patches for the two most recent minor 6 | versions. For example, if Livebook v0.12.0 is released, we will 7 | backport security fixes for v0.12.x and v0.11.x. Once Livebook reaches 8 | v1.0, we plan to introduce more long-term security guarantees. 9 | If you have any questions, please reach our to them on Discussion page. 10 | 11 | ## Reporting a Vulnerability 12 | 13 | To report a security vulnerability, you can send an email to `security@livebook.dev` 14 | or [privately disclose it on our GitHub page](https://github.com/livebook-dev/livebook/security). 15 | If you don't receive an acknowledgement of the report within 24h, 16 | feel free to ping us in our public forums (but without disclosing 17 | the contents of the vulnerability). 18 | -------------------------------------------------------------------------------- /assets/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } 4 | -------------------------------------------------------------------------------- /assets/css/app.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss/base"; 2 | @import "tailwindcss/components"; 3 | @import "tailwindcss/utilities"; 4 | 5 | @import "./components.css"; 6 | @import "./utilities.css"; 7 | @import "./live_view.css"; 8 | @import "./markdown.css"; 9 | @import "./ansi.css"; 10 | @import "./js_interop.css"; 11 | @import "./tooltips.css"; 12 | @import "./notebook.css"; 13 | -------------------------------------------------------------------------------- /assets/css/live_view.css: -------------------------------------------------------------------------------- 1 | /* Hide Phoenix live reload frame */ 2 | iframe[hidden] { 3 | display: none; 4 | } 5 | 6 | /* LiveView specific classes */ 7 | 8 | .phx-no-feedback.invalid-feedback, 9 | .phx-no-feedback .invalid-feedback { 10 | display: none; 11 | } 12 | 13 | .phx-click-loading { 14 | opacity: 0.5; 15 | transition: opacity 1s ease-out; 16 | } 17 | 18 | .phx-loading { 19 | cursor: wait; 20 | } 21 | -------------------------------------------------------------------------------- /assets/css/notebook.css: -------------------------------------------------------------------------------- 1 | /* 2 | Specific icons used in the built-in notebooks 3 | 4 | Note: we use the ri- prefix to use the existing 5 | Remix icon CSS 6 | */ 7 | 8 | .ri-livebook-sections, 9 | .ri-livebook-runtime, 10 | .ri-livebook-shortcuts, 11 | .ri-livebook-secrets, 12 | .ri-livebook-deploy, 13 | .ri-livebook-terminal, 14 | .ri-livebook-save { 15 | @apply text-xl align-middle; 16 | } 17 | 18 | .ri-livebook-sections:before { 19 | content: "\eade"; 20 | } 21 | 22 | .ri-livebook-runtime:before { 23 | content: "\ebf0"; 24 | } 25 | 26 | .ri-livebook-shortcuts:before { 27 | content: "\ee72"; 28 | } 29 | 30 | .ri-livebook-secrets:before { 31 | content: "\eed0"; 32 | } 33 | 34 | .ri-livebook-deploy:before { 35 | content: "\f096"; 36 | } 37 | 38 | .ri-livebook-terminal:before { 39 | content: "\f1f8"; 40 | } 41 | 42 | .ri-livebook-save:before { 43 | content: "\f0b3"; 44 | } 45 | -------------------------------------------------------------------------------- /assets/js/confirm.js: -------------------------------------------------------------------------------- 1 | import { load, store } from "./lib/storage"; 2 | 3 | const OPT_OUT_IDS_KEY = "confirm-opted-out-ids"; 4 | 5 | export function loadConfirmOptOutIds() { 6 | return load(OPT_OUT_IDS_KEY) || []; 7 | } 8 | 9 | export function registerGlobalEventHandlersForConfirm() { 10 | window.addEventListener("phx:add_confirm_opt_out_id", (event) => { 11 | const optedOutIds = load(OPT_OUT_IDS_KEY) || []; 12 | const optOutId = event.detail.opt_out_id; 13 | optedOutIds.push(optOutId); 14 | store(OPT_OUT_IDS_KEY, optedOutIds); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /assets/js/dom.js: -------------------------------------------------------------------------------- 1 | export const morphdomOptions = { 2 | onBeforeElUpdated(from, to) { 3 | // Keep element attributes starting with data-js- 4 | // which we set on the client. 5 | for (const attr of from.attributes) { 6 | if (attr.name.startsWith("data-js-")) { 7 | to.setAttribute(attr.name, attr.value); 8 | } 9 | 10 | if (attr.name === "data-keep-attribute") { 11 | if (from.hasAttribute(attr.value)) { 12 | to.setAttribute(attr.value, from.getAttribute(attr.value)); 13 | } else { 14 | to.removeAttribute(attr.value); 15 | } 16 | } 17 | } 18 | }, 19 | 20 | onNodeAdded(node) { 21 | // Mimic autofocus for dynamically inserted elements 22 | if (node.nodeType === Node.ELEMENT_NODE && node.hasAttribute("autofocus")) { 23 | node.focus(); 24 | 25 | if (node.setSelectionRange && node.value) { 26 | const lastIndex = node.value.length; 27 | node.setSelectionRange(lastIndex, lastIndex); 28 | } 29 | } 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /assets/js/hooks/app_auth.js: -------------------------------------------------------------------------------- 1 | import { storeAppAuthToken } from "../lib/app"; 2 | 3 | /** 4 | * A hook for the app auth page. 5 | */ 6 | const AppAuth = { 7 | mounted() { 8 | this.handleEvent("persist_app_auth", ({ slug, token }) => { 9 | storeAppAuthToken(slug, token); 10 | this.pushEvent("app_auth_persisted"); 11 | }); 12 | }, 13 | }; 14 | 15 | export default AppAuth; 16 | -------------------------------------------------------------------------------- /assets/js/hooks/cell_editor/live_editor/codemirror/commands.js: -------------------------------------------------------------------------------- 1 | import { closeCompletion } from "@codemirror/autocomplete"; 2 | import { insertBlankLine } from "@codemirror/commands"; 3 | import { closeSignature } from "./signature"; 4 | 5 | /** 6 | * This command, when multi-cursor is active, collapses the selection 7 | * to the main cursor only. 8 | */ 9 | export function exitMulticursor(view) { 10 | const selection = view.state.selection; 11 | 12 | if (selection.ranges.length > 1) { 13 | view.dispatch({ selection: selection.asSingle() }); 14 | return true; 15 | } 16 | 17 | return false; 18 | } 19 | 20 | /** 21 | * Calls `insertBlankLine` and closes active intellisense hints. 22 | */ 23 | export function insertBlankLineAndCloseHints(view) { 24 | if (insertBlankLine(view)) { 25 | closeCompletion(view); 26 | closeSignature(view); 27 | return true; 28 | } 29 | 30 | return false; 31 | } 32 | -------------------------------------------------------------------------------- /assets/js/hooks/cell_editor/live_editor/codemirror/hover_details.js: -------------------------------------------------------------------------------- 1 | import { Decoration, EditorView, hoverTooltip } from "@codemirror/view"; 2 | import { RangeSet } from "@codemirror/state"; 3 | 4 | /** 5 | * Returns an extension that enables hover details tooltip. 6 | */ 7 | export function hoverDetails(hoverTooltipSource) { 8 | const tooltipExtension = hoverTooltip(hoverTooltipSource); 9 | 10 | const decorationsExtension = EditorView.decorations.from( 11 | tooltipExtension.active, 12 | (tooltips) => { 13 | const backgroundDecoration = Decoration.mark({ 14 | class: "cm-hoverDocsSelection", 15 | }); 16 | 17 | const decorationRanges = tooltips.map((tooltip) => 18 | backgroundDecoration.range(tooltip.pos, tooltip.end), 19 | ); 20 | 21 | return RangeSet.of(decorationRanges, true); 22 | }, 23 | ); 24 | 25 | return [tooltipExtension, decorationsExtension]; 26 | } 27 | -------------------------------------------------------------------------------- /assets/js/hooks/cell_editor/live_editor/codemirror/selecting_class.js: -------------------------------------------------------------------------------- 1 | import { EditorView } from "@codemirror/view"; 2 | 3 | const attributesFacet = EditorView.editorAttributes.compute( 4 | ["selection"], 5 | (state) => { 6 | const allRangesEmpty = state.selection.ranges.every((range) => range.empty); 7 | return allRangesEmpty ? {} : { class: "cm-selecting" }; 8 | }, 9 | ); 10 | 11 | /** 12 | * Returns an extension that adds `cm-selecting` class to the root 13 | * element, whenever the editor has a non-empty selection range. 14 | */ 15 | export function selectingClass() { 16 | return [attributesFacet]; 17 | } 18 | -------------------------------------------------------------------------------- /assets/js/hooks/cell_editor/live_editor/codemirror/toggle_with.js: -------------------------------------------------------------------------------- 1 | import { EditorView, keymap } from "@codemirror/view"; 2 | import { Compartment } from "@codemirror/state"; 3 | 4 | /** 5 | * Returns an extension that toggles the given extension with the given 6 | * keyboard shortcut. 7 | */ 8 | export function toggleWith(key, extension) { 9 | const compartment = new Compartment(); 10 | 11 | function toggle(view) { 12 | const isEnabled = compartment.get(view.state) === extension; 13 | 14 | view.dispatch({ 15 | effects: compartment.reconfigure(isEnabled ? [] : extension), 16 | }); 17 | 18 | return true; 19 | } 20 | 21 | return [compartment.of([]), keymap.of({ key, run: toggle })]; 22 | } 23 | -------------------------------------------------------------------------------- /assets/js/hooks/cell_editor/live_editor/codemirror/tree_utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Finds the closest node with the given name (parent or self). 3 | */ 4 | export function closestNode(node, names) { 5 | while (node) { 6 | if (names.includes(node.type.name)) return node; 7 | 8 | node = node.parent; 9 | } 10 | 11 | return null; 12 | } 13 | 14 | /** 15 | * Goes up the tree using the given path. 16 | * 17 | * Path is a list of parent node names, from innermost to outermost. 18 | * Returns the alst node on the path, but only if the path matches 19 | * exactly. 20 | */ 21 | export function ancestorNode(node, path) { 22 | let i = 0; 23 | 24 | while (i < path.length && node.parent) { 25 | if (node.parent.type.name !== path[i]) return null; 26 | 27 | node = node.parent; 28 | i++; 29 | } 30 | 31 | return i === path.length && node ? node : null; 32 | } 33 | -------------------------------------------------------------------------------- /assets/js/hooks/dropzone.js: -------------------------------------------------------------------------------- 1 | const DRAGGING_ATTR = "data-js-dragging"; 2 | 3 | /** 4 | * A hook used to highlight drop zone when dragging a file. 5 | */ 6 | const Dropzone = { 7 | mounted() { 8 | this.el.addEventListener("dragenter", (event) => { 9 | this.el.setAttribute(DRAGGING_ATTR, ""); 10 | }); 11 | 12 | this.el.addEventListener("dragleave", (event) => { 13 | if (!this.el.contains(event.relatedTarget)) { 14 | this.el.removeAttribute(DRAGGING_ATTR); 15 | } 16 | }); 17 | 18 | this.el.addEventListener("drop", (event) => { 19 | this.el.removeAttribute(DRAGGING_ATTR); 20 | }); 21 | }, 22 | }; 23 | 24 | export default Dropzone; 25 | -------------------------------------------------------------------------------- /assets/js/hooks/emoji_picker.js: -------------------------------------------------------------------------------- 1 | import { createPopup } from "@picmo/popup-picker"; 2 | 3 | /** 4 | * A hook for the emoji picker input. 5 | */ 6 | const EmojiPicker = { 7 | mounted() { 8 | const button = this.el.querySelector("[data-emoji-button]"); 9 | const preview = this.el.querySelector("[data-emoji-preview]"); 10 | const input = this.el.querySelector("[data-emoji-input]"); 11 | 12 | const picker = createPopup( 13 | { showPreview: false }, 14 | { 15 | triggerElement: button, 16 | referenceElement: button, 17 | position: "bottom", 18 | }, 19 | ); 20 | 21 | picker.addEventListener("emoji:select", ({ emoji }) => { 22 | preview.innerHTML = emoji; 23 | input.value = emoji; 24 | }); 25 | 26 | button.addEventListener("click", (_) => { 27 | picker.toggle(); 28 | }); 29 | }, 30 | }; 31 | 32 | export default EmojiPicker; 33 | -------------------------------------------------------------------------------- /assets/js/hooks/focus_on_update.js: -------------------------------------------------------------------------------- 1 | import { isEditableElement } from "../lib/utils"; 2 | 3 | /** 4 | * A hook used to focus an element whenever it receives LV update. 5 | */ 6 | const FocusOnUpdate = { 7 | mounted() { 8 | this.focus(); 9 | }, 10 | 11 | updated() { 12 | if (this.el !== document.activeElement) { 13 | this.focus(); 14 | } 15 | }, 16 | 17 | focus() { 18 | if (isEditableElement(document.activeElement)) { 19 | return; 20 | } 21 | 22 | this.el.focus(); 23 | this.el.selectionStart = this.el.selectionEnd = this.el.value.length; 24 | this.el.scrollLeft = this.el.scrollWidth; 25 | }, 26 | }; 27 | 28 | export default FocusOnUpdate; 29 | -------------------------------------------------------------------------------- /assets/js/hooks/highlight.js: -------------------------------------------------------------------------------- 1 | import { parseHookProps } from "../lib/attribute"; 2 | import { highlight } from "./cell_editor/live_editor/highlight"; 3 | import { findChildOrThrow } from "../lib/utils"; 4 | 5 | /** 6 | * A hook used to highlight source code in the root element. 7 | * 8 | * ## Props 9 | * 10 | * * `language` - language of the source code 11 | * 12 | * ## Children 13 | * 14 | * * `[data-source]` - an element containing the source code to be 15 | * highlighted 16 | * 17 | * * `[data-target]` - the element to render highlighted code into 18 | * 19 | */ 20 | const Highlight = { 21 | mounted() { 22 | this.props = this.getProps(); 23 | 24 | this.sourceEl = findChildOrThrow(this.el, "[data-source]"); 25 | this.targetEl = findChildOrThrow(this.el, "[data-target]"); 26 | 27 | this.updateDOM(); 28 | }, 29 | 30 | updated() { 31 | this.props = this.getProps(); 32 | this.updateDOM(); 33 | }, 34 | 35 | getProps() { 36 | return parseHookProps(this.el, ["language"]); 37 | }, 38 | 39 | updateDOM() { 40 | const code = this.sourceEl.innerText; 41 | 42 | const html = highlight(code, this.props.language); 43 | this.targetEl.innerHTML = html; 44 | this.sourceEl.style.display = "none"; 45 | }, 46 | }; 47 | 48 | export default Highlight; 49 | -------------------------------------------------------------------------------- /assets/js/hooks/scroll_on_update.js: -------------------------------------------------------------------------------- 1 | import { scrollToEnd } from "../lib/utils"; 2 | 3 | /** 4 | * A hook used to scroll to the bottom of an element whenever it 5 | * receives LV update. 6 | */ 7 | const ScrollOnUpdate = { 8 | mounted() { 9 | this.scroll(); 10 | }, 11 | 12 | updated() { 13 | this.scroll(); 14 | }, 15 | 16 | scroll() { 17 | scrollToEnd(this.el); 18 | }, 19 | }; 20 | 21 | export default ScrollOnUpdate; 22 | -------------------------------------------------------------------------------- /assets/js/hooks/textarea_autosize.js: -------------------------------------------------------------------------------- 1 | const BORDER_HEIGHT = 1; 2 | 3 | /** 4 | * A hook that automatically matches textarea height to its content. 5 | */ 6 | const TextareaAutosize = { 7 | mounted() { 8 | this.autosize(); 9 | 10 | this.el.addEventListener("input", (event) => { 11 | this.autosize(); 12 | }); 13 | }, 14 | 15 | updated() { 16 | this.autosize(); 17 | }, 18 | 19 | autosize() { 20 | this.el.style.height = "0px"; 21 | this.el.style.height = `${this.el.scrollHeight + 2 * BORDER_HEIGHT}px`; 22 | }, 23 | }; 24 | 25 | export default TextareaAutosize; 26 | -------------------------------------------------------------------------------- /assets/js/hooks/timer.js: -------------------------------------------------------------------------------- 1 | import { parseHookProps } from "../lib/attribute"; 2 | 3 | const UPDATE_INTERVAL_MS = 100; 4 | 5 | /** 6 | * A hook used to display a counting timer. 7 | * 8 | * ## Props 9 | * 10 | * * `start` - the timestamp to count from 11 | * 12 | */ 13 | const Timer = { 14 | mounted() { 15 | this.props = this.getProps(); 16 | 17 | this.interval = setInterval(() => this.updateDOM(), UPDATE_INTERVAL_MS); 18 | }, 19 | 20 | updated() { 21 | this.props = this.getProps(); 22 | this.updateDOM(); 23 | }, 24 | 25 | destroyed() { 26 | clearInterval(this.interval); 27 | }, 28 | 29 | getProps() { 30 | return parseHookProps(this.el, ["start"]); 31 | }, 32 | 33 | updateDOM() { 34 | const elapsedMs = Date.now() - new Date(this.props.start); 35 | const elapsedSeconds = elapsedMs / 1_000; 36 | 37 | this.el.innerHTML = `${elapsedSeconds.toFixed(1)}s`; 38 | }, 39 | }; 40 | 41 | export default Timer; 42 | -------------------------------------------------------------------------------- /assets/js/hooks/user_form.js: -------------------------------------------------------------------------------- 1 | import { storeUserData } from "../lib/user"; 2 | 3 | /** 4 | * A hook for the user profile form. 5 | * 6 | * On submit this hook saves the new data into cookie. This cookie 7 | * serves as a backup and can be used to restore user data if the 8 | * server is restarted. 9 | */ 10 | const UserForm = { 11 | mounted() { 12 | this.el.addEventListener("submit", (event) => { 13 | const name = this.el.user_form_name.value; 14 | const hex_color = this.el.user_form_hex_color.value; 15 | storeUserData({ name, hex_color }); 16 | }); 17 | }, 18 | }; 19 | 20 | export default UserForm; 21 | -------------------------------------------------------------------------------- /assets/js/lib/app.js: -------------------------------------------------------------------------------- 1 | import { load, store } from "./storage"; 2 | 3 | const APP_AUTH_TOKEN_PREFIX = "app_auth_token:"; 4 | 5 | export function storeAppAuthToken(slug, token) { 6 | store(APP_AUTH_TOKEN_PREFIX + slug, token); 7 | } 8 | 9 | export function loadAppAuthToken() { 10 | const path = window.location.pathname; 11 | 12 | if (path.startsWith("/apps/")) { 13 | const slug = path.split("/")[2]; 14 | const token = load(APP_AUTH_TOKEN_PREFIX + slug); 15 | 16 | if (token) { 17 | return token; 18 | } 19 | } 20 | 21 | return null; 22 | } 23 | -------------------------------------------------------------------------------- /assets/js/lib/attribute.js: -------------------------------------------------------------------------------- 1 | export function parseHookProps(element, names) { 2 | const props = {}; 3 | 4 | for (const name of names) { 5 | const attr = `data-p-${name}`; 6 | 7 | if (!element.hasAttribute(attr)) { 8 | throw new Error( 9 | `Missing attribute "${attr}" on element <${element.tagName}:${element.id}>`, 10 | ); 11 | } 12 | 13 | const value = element.getAttribute(attr); 14 | props[kebabToCamelCase(name)] = JSON.parse(value); 15 | } 16 | 17 | return props; 18 | } 19 | 20 | function kebabToCamelCase(name) { 21 | const [part, ...parts] = name.split("-"); 22 | 23 | return [ 24 | part, 25 | ...parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)), 26 | ].join(""); 27 | } 28 | -------------------------------------------------------------------------------- /assets/js/lib/cache_lru.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A Map-based LRU cache. 3 | */ 4 | export default class CacheLRU { 5 | constructor(size) { 6 | this.size = size; 7 | this.cache = new Map(); 8 | } 9 | 10 | get(key) { 11 | if (this.cache.has(key)) { 12 | const value = this.cache.get(key); 13 | // Map keys are stored and iterated in insertion order, 14 | // so we reinsert on every access 15 | this.cache.delete(key); 16 | this.cache.set(key, value); 17 | return value; 18 | } else { 19 | return undefined; 20 | } 21 | } 22 | 23 | set(key, value) { 24 | if (this.cache.has(key)) { 25 | this.cache.delete(key); 26 | } else if (this.cache.size === this.size) { 27 | const oldestKey = this.cache.keys().next().value; 28 | this.cache.delete(oldestKey); 29 | } 30 | 31 | this.cache.set(key, value); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /assets/js/lib/emitter.js: -------------------------------------------------------------------------------- 1 | /** 2 | * An abstraction for registering and dispatching callbacks. 3 | */ 4 | export default class Emitter { 5 | constructor() { 6 | /** @private */ 7 | this.callbacks = []; 8 | } 9 | 10 | /** 11 | * A function used to register new listener in the emitter. 12 | * 13 | * This is a shorthand for `addListener`. 14 | */ 15 | get event() { 16 | return this.addListener.bind(this); 17 | } 18 | 19 | /** 20 | * Adds new listener to the emitter. 21 | * 22 | * Returns a subscription object that you can destroy in order to 23 | * unsubscribe. 24 | */ 25 | addListener(callback) { 26 | this.callbacks.push(callback); 27 | 28 | return { 29 | destroy: () => { 30 | this.removeListener(callback); 31 | }, 32 | }; 33 | } 34 | 35 | /** 36 | * Removes a listener from the emitter. 37 | */ 38 | removeListener(callback) { 39 | const idx = this.callbacks.indexOf(callback); 40 | 41 | if (idx !== -1) { 42 | this.callbacks.splice(idx, 1); 43 | } 44 | } 45 | 46 | /** 47 | * Dispatches all listeners with the given arguments. 48 | */ 49 | dispatch(...args) { 50 | this.callbacks.forEach((callback) => { 51 | callback(...args); 52 | }); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /assets/js/lib/notebook.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if the given cell type is eligible for evaluation. 3 | */ 4 | export function isEvaluable(cellType) { 5 | return ["code", "smart"].includes(cellType); 6 | } 7 | 8 | /** 9 | * Checks if the given cell type has primary editable editor. 10 | */ 11 | export function isDirectlyEditable(cellType) { 12 | return ["markdown", "code"].includes(cellType); 13 | } 14 | -------------------------------------------------------------------------------- /assets/js/lib/storage.js: -------------------------------------------------------------------------------- 1 | const PREFIX = "livebook:"; 2 | 3 | /** 4 | * Loads value from local storage. 5 | */ 6 | export function load(key) { 7 | try { 8 | const json = localStorage.getItem(PREFIX + key); 9 | 10 | if (json) { 11 | return JSON.parse(json); 12 | } 13 | } catch (error) { 14 | console.error( 15 | `Failed to load from local storage, reason: ${error.message}`, 16 | ); 17 | } 18 | 19 | return undefined; 20 | } 21 | 22 | /** 23 | * Stores value in local storage. 24 | * 25 | * The value is serialized as JSON. 26 | */ 27 | export function store(key, value) { 28 | try { 29 | const json = JSON.stringify(value); 30 | localStorage.setItem(PREFIX + key, json); 31 | } catch (error) { 32 | console.error(`Failed to write to local storage, reason: ${error.message}`); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /assets/js/lib/text_utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns length of suffix in `string` that should be replaced 3 | * with `newSuffix` to avoid duplication. 4 | */ 5 | export function replacedSuffixLength(string, newSuffix) { 6 | let suffix = newSuffix; 7 | 8 | while (!string.endsWith(suffix)) { 9 | suffix = suffix.slice(0, -1); 10 | } 11 | 12 | return suffix.length; 13 | } 14 | -------------------------------------------------------------------------------- /assets/js/lib/user.js: -------------------------------------------------------------------------------- 1 | import { cookieOptions, decodeBase64, encodeBase64 } from "./utils"; 2 | 3 | const USER_DATA_COOKIE = "lb_user_data"; 4 | 5 | /** 6 | * Stores user data in the `"lb_user_data"` cookie. 7 | */ 8 | export function storeUserData(userData) { 9 | const json = JSON.stringify(userData); 10 | const encoded = encodeBase64(json); 11 | setCookie(USER_DATA_COOKIE, encoded, 157_680_000); // 5 years 12 | } 13 | 14 | /** 15 | * Loads user data from the `"lb_user_data"` cookie. 16 | */ 17 | export function loadUserData() { 18 | const encoded = getCookieValue(USER_DATA_COOKIE); 19 | if (encoded) { 20 | const json = decodeBase64(encoded); 21 | return JSON.parse(json); 22 | } else { 23 | return null; 24 | } 25 | } 26 | 27 | function getCookieValue(key) { 28 | const cookie = document.cookie 29 | .split("; ") 30 | .find((cookie) => cookie.startsWith(`${key}=`)); 31 | 32 | if (cookie) { 33 | const value = cookie.replace(`${key}=`, ""); 34 | return value; 35 | } else { 36 | return null; 37 | } 38 | } 39 | 40 | function setCookie(key, value, maxAge) { 41 | const cookie = `${key}=${value};max-age=${maxAge};path=/${cookieOptions()}`; 42 | document.cookie = cookie; 43 | } 44 | -------------------------------------------------------------------------------- /assets/test/lib/emitter.test.js: -------------------------------------------------------------------------------- 1 | import Emitter from "../../js/lib/emitter"; 2 | 3 | test("listener callbacks are called on dispatch", () => { 4 | const emitter = new Emitter(); 5 | const callback1 = jest.fn(); 6 | const callback2 = jest.fn(); 7 | 8 | emitter.addListener(callback1); 9 | emitter.addListener(callback2); 10 | emitter.dispatch({ data: 1 }); 11 | 12 | expect(callback1).toHaveBeenCalledWith({ data: 1 }); 13 | expect(callback2).toHaveBeenCalledWith({ data: 1 }); 14 | }); 15 | 16 | test("addListener returns a subscription object that can be destroyed", () => { 17 | const emitter = new Emitter(); 18 | const callback1 = jest.fn(); 19 | 20 | const subscription = emitter.addListener(callback1); 21 | subscription.destroy(); 22 | emitter.dispatch({}); 23 | 24 | expect(callback1).not.toHaveBeenCalled(); 25 | }); 26 | -------------------------------------------------------------------------------- /assets/test/lib/pubsub.test.js: -------------------------------------------------------------------------------- 1 | import PubSub from "../../js/lib/pubsub"; 2 | 3 | test("subscribed callback is called on the specified topic", () => { 4 | const pubsub = new PubSub(); 5 | const callback1 = jest.fn(); 6 | const callback2 = jest.fn(); 7 | 8 | pubsub.subscribe("topic1", callback1); 9 | pubsub.subscribe("topic2", callback2); 10 | pubsub.broadcast("topic1", { data: 1 }); 11 | 12 | expect(callback1).toHaveBeenCalledWith({ data: 1 }); 13 | expect(callback2).not.toHaveBeenCalled(); 14 | }); 15 | 16 | test("subscribe returns a subscription object that can be destroyed", () => { 17 | const pubsub = new PubSub(); 18 | const callback1 = jest.fn(); 19 | 20 | const subscription = pubsub.subscribe("topic1", callback1); 21 | subscription.destroy(); 22 | pubsub.broadcast("topic1", {}); 23 | 24 | expect(callback1).not.toHaveBeenCalled(); 25 | }); 26 | -------------------------------------------------------------------------------- /assets/test/lib/text_utils.test.js: -------------------------------------------------------------------------------- 1 | import { replacedSuffixLength } from "../../js/lib/text_utils"; 2 | 3 | test("replacedSuffixLength", () => { 4 | expect(replacedSuffixLength("to_string(", "")).toEqual(0); 5 | expect(replacedSuffixLength("to_string(", "length")).toEqual(0); 6 | expect(replacedSuffixLength("length", "length")).toEqual(6); 7 | expect(replacedSuffixLength("x = ~", "~r")).toEqual(1); 8 | expect(replacedSuffixLength("Enum.ma", "map")).toEqual(2); 9 | expect(replacedSuffixLength("Enum.ma", "map_reduce")).toEqual(2); 10 | }); 11 | -------------------------------------------------------------------------------- /config/runtime.exs: -------------------------------------------------------------------------------- 1 | import Config 2 | Livebook.config_runtime() 3 | -------------------------------------------------------------------------------- /config/test.exs: -------------------------------------------------------------------------------- 1 | import Config 2 | 3 | # We don't run a server during test. If one is required, 4 | # you can enable the server option below. 5 | config :livebook, LivebookWeb.Endpoint, 6 | http: [port: 4002], 7 | server: false 8 | 9 | # Print only warnings and errors during test 10 | config :logger, level: :warning 11 | 12 | # Disable authentication in tests 13 | config :livebook, 14 | authentication: :disabled, 15 | check_completion_data_interval: 300, 16 | iframe_port: 4003 17 | 18 | data_path = Path.expand("tmp/livebook_data/test") 19 | 20 | # Clear data path for tests 21 | if File.exists?(data_path) do 22 | File.rm_rf!(data_path) 23 | end 24 | 25 | config :livebook, 26 | data_path: data_path, 27 | agent_name: "chonky-cat", 28 | k8s_kubeconfig_pipeline: 29 | {Kubereq.Kubeconfig.Stub, 30 | plugs: %{ 31 | "default" => {Req.Test, :k8s_cluster}, 32 | "no-permission" => {Req.Test, :k8s_cluster} 33 | }} 34 | 35 | config :livebook, Livebook.Apps.Manager, retry_backoff_base_ms: 0 36 | -------------------------------------------------------------------------------- /docs/images/add_app_server_to_deployment_group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/add_app_server_to_deployment_group.png -------------------------------------------------------------------------------- /docs/images/add_shared_file_storage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/add_shared_file_storage.png -------------------------------------------------------------------------------- /docs/images/add_shared_secret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/add_shared_secret.png -------------------------------------------------------------------------------- /docs/images/add_shared_secret_from_notebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/add_shared_secret_from_notebook.png -------------------------------------------------------------------------------- /docs/images/app_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/app_preview.png -------------------------------------------------------------------------------- /docs/images/app_server_authentication.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/app_server_authentication.png -------------------------------------------------------------------------------- /docs/images/app_server_docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/app_server_docker.png -------------------------------------------------------------------------------- /docs/images/app_server_setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/app_server_setup.png -------------------------------------------------------------------------------- /docs/images/app_server_setup_message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/app_server_setup_message.png -------------------------------------------------------------------------------- /docs/images/auth_via_teams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/auth_via_teams.png -------------------------------------------------------------------------------- /docs/images/deploy_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/deploy_button.png -------------------------------------------------------------------------------- /docs/images/deployed_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/deployed_app.png -------------------------------------------------------------------------------- /docs/images/deployed_apps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/deployed_apps.png -------------------------------------------------------------------------------- /docs/images/deployment_group_form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/deployment_group_form.png -------------------------------------------------------------------------------- /docs/images/deployment_groups_inside_workspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/deployment_groups_inside_workspace.png -------------------------------------------------------------------------------- /docs/images/email_domain_auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/email_domain_auth.png -------------------------------------------------------------------------------- /docs/images/github_stars_notebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/github_stars_notebook.png -------------------------------------------------------------------------------- /docs/images/instructions_setup_app_server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/instructions_setup_app_server.png -------------------------------------------------------------------------------- /docs/images/oidc_groups_auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/docs/images/oidc_groups_auth.png -------------------------------------------------------------------------------- /docs/teams/intro_to_teams.md: -------------------------------------------------------------------------------- 1 | # Intro to Livebook Teams 2 | 3 | [Livebook Teams](https://livebook.dev/teams/?ref=docs) enables you to deploy Livebook apps to your own infrastructure. 4 | 5 | 6 | 7 | Besides deployment automation, it also offers the following features: 8 | 9 | - Authentication via multiple identity providers: 10 | - Livebook Teams 11 | - [Email-domain (Google, Microsoft, etc)](email_domain.md) 12 | - [OIDC (Okta, Microsoft Entra, etc)](oidc_sso.md) 13 | - Authorization 14 | - [based on groups from an OIDC identity provider (Okta, Microsoft Entra, etc)](oidc_groups.md) 15 | - [Shared Livebook secrets](shared_secrets.md) 16 | - [Shared Livebook file storages](shared_file_storages.md) 17 | 18 | You can [start a free trial here](https://livebook.dev/teams/?ref=docs). 19 | -------------------------------------------------------------------------------- /docs/teams/shared_file_storages.md: -------------------------------------------------------------------------------- 1 | # Shared file storages 2 | 3 | ## Overview 4 | 5 | This feature allows your team to share the configuration of S3-compatible storages. 6 | 7 | Livebook file storages are used to store notebooks and their files. 8 | 9 | Whenever you add (update or detach) a file storage to your organization workspace, Livebook Teams will synchronize that with the Livebook of every member of your organization. 10 | 11 | ![](images/add_shared_file_storage.png) 12 | 13 | ## How it works 14 | 15 | Here's a video showing how that feature works. 16 | 17 | 18 | 19 | ## Security strengths 20 | 21 | Livebook Teams cannot access the credentials of your S3 (compatible) account. 22 | 23 | Livebook encrypts your S3 credentials locally in your machine using your Teams key. Then, they're sent encrypted to Livebook Teams servers. 24 | 25 | When a new synchronization is needed, Livebook Teams sends the encrypted credentials to the Livebook of team members, and their Livebook decrypts that in their local machines using the same Teams key. 26 | -------------------------------------------------------------------------------- /elixirkit/.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /elixirkit/.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where third-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | elixirkit-*.tar 24 | 25 | # Temporary files, for example, from tests. 26 | /tmp/ 27 | -------------------------------------------------------------------------------- /elixirkit/demo/.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /elixirkit/demo/.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where third-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | demo-*.tar 24 | 25 | # Temporary files, for example, from tests. 26 | /tmp/ 27 | -------------------------------------------------------------------------------- /elixirkit/demo/README.md: -------------------------------------------------------------------------------- 1 | # Demo 2 | 3 | This is an example ElixirKit application. 4 | 5 | See: 6 | 7 | * [Elixir Server](lib/demo.ex) 8 | * [Swift Client](rel/swift) 9 | * [AppKit Client](rel/appkit) 10 | * [C# Client](rel/dotnet) 11 | * [Windows Forms Client](rel/winforms) 12 | -------------------------------------------------------------------------------- /elixirkit/demo/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Demo.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :demo, 7 | version: "0.1.0", 8 | elixir: "~> 1.14", 9 | start_permanent: Mix.env() == :prod, 10 | deps: deps() 11 | ] 12 | end 13 | 14 | def application do 15 | [ 16 | extra_applications: [:logger], 17 | mod: {Demo.Application, []} 18 | ] 19 | end 20 | 21 | defp deps do 22 | [ 23 | {:elixirkit, path: ".."} 24 | ] 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/appkit/.gitignore: -------------------------------------------------------------------------------- 1 | /.build 2 | /Packages 3 | /*.xcodeproj 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/config/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/appkit/App.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-jit 6 | 7 | com.apple.security.cs.allow-unsigned-executable-memory 8 | 9 | com.apple.security.cs.allow-dyld-environment-variables 10 | 11 | com.apple.security.cs.disable-library-validation 12 | 13 | 14 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/appkit/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.5 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Demo", 7 | platforms: [ 8 | .macOS(.v11) 9 | ], 10 | dependencies: [ 11 | .package(name: "ElixirKit", path: "../../../elixirkit_swift") 12 | ], 13 | targets: [ 14 | .executableTarget( 15 | name: "Demo", 16 | dependencies: ["ElixirKit"] 17 | ) 18 | ] 19 | ) 20 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/appkit/README.md: -------------------------------------------------------------------------------- 1 | # Demo 2 | 3 | Run the app, just the executable: 4 | 5 | $ ./run.sh 6 | 7 | Run the app bundle: 8 | 9 | $ ./run_app.sh 10 | 11 | Build the .dmg: 12 | 13 | $ ./build_dmg.sh 14 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/appkit/build_app.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | export MIX_ENV=prod 5 | export ELIXIRKIT_APP_NAME=Demo 6 | export ELIXIRKIT_PROJECT_DIR=$PWD/../../.. 7 | 8 | . ../../../elixirkit_swift/Scripts/build_macos_app.sh 9 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/appkit/build_dmg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | . `dirname $0`/build_app.sh 5 | . ../../../elixirkit_swift/Scripts/build_macos_dmg.sh 6 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/appkit/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | swift build 5 | target_dir=`swift build --show-bin-path` 6 | (cd ../.. && mix release --overwrite --path=$target_dir/rel) 7 | $target_dir/Demo 8 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/appkit/run_app.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | export ELIXIRKIT_APP_NAME=Demo 5 | export ELIXIRKIT_PROJECT_DIR=$PWD/../.. 6 | 7 | . ../../../../elixirkit/elixirkit_swift/Scripts/build_macos_app.sh 8 | open -W --stdout `tty` --stderr `tty` .build/Demo.app 9 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/dotnet/.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /obj 3 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/dotnet/Demo.cs: -------------------------------------------------------------------------------- 1 | class Demo 2 | { 3 | public static void Main() 4 | { 5 | ElixirKit.API.Start( 6 | name: "demo", 7 | ready: () => 8 | { 9 | ElixirKit.API.Publish("log", "Hello from C#!"); 10 | 11 | ElixirKit.API.Subscribe((name, data) => 12 | { 13 | switch (name) 14 | { 15 | case "log": 16 | Console.WriteLine($"[client] {data}"); 17 | break; 18 | 19 | default: 20 | throw new Exception($"unknown event {name}"); 21 | } 22 | }); 23 | } 24 | ); 25 | 26 | System.Console.WriteLine($"{ElixirKit.API.WaitForExit()}"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/dotnet/Demo.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/dotnet/README.md: -------------------------------------------------------------------------------- 1 | # Demo 2 | 3 | Run the app: 4 | 5 | $ ./run.sh 6 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/dotnet/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | dotnet build 5 | target_dir="$PWD/bin/Debug/net6.0" 6 | (cd ../.. && mix release --overwrite --path=${target_dir}/rel) 7 | dotnet run --no-build 8 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/swift/.gitignore: -------------------------------------------------------------------------------- 1 | /.build 2 | /Packages 3 | /*.xcodeproj 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/config/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/swift/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.5 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Demo", 7 | platforms: [ 8 | .macOS(.v11) 9 | ], 10 | dependencies: [ 11 | .package(name: "ElixirKit", path: "../../../elixirkit_swift") 12 | ], 13 | targets: [ 14 | .executableTarget( 15 | name: "Demo", 16 | dependencies: ["ElixirKit"] 17 | ) 18 | ] 19 | ) 20 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/swift/README.md: -------------------------------------------------------------------------------- 1 | # Demo 2 | 3 | Run the app: 4 | 5 | $ ./run.sh 6 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/swift/Sources/Demo/Demo.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import ElixirKit 3 | 4 | @main 5 | struct Demo { 6 | public static func main() { 7 | // Capture ctrl+c 8 | signal(SIGINT) { signal in 9 | ElixirKit.API.stop() 10 | exit(signal) 11 | } 12 | 13 | ElixirKit.API.start( 14 | name: "demo", 15 | readyHandler: { 16 | ElixirKit.API.publish("log", "Hello from Swift!") 17 | 18 | ElixirKit.API.addObserver(queue: .main) { (name, data) in 19 | switch name { 20 | case "log": 21 | print("[client] " + data) 22 | default: 23 | fatalError("unknown event \(name)") 24 | } 25 | } 26 | } 27 | ) 28 | 29 | ElixirKit.API.waitUntilExit() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/swift/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | swift build 5 | target_dir=`swift build --show-bin-path` 6 | (cd ../.. && mix release --overwrite --path=$target_dir/rel) 7 | $target_dir/Demo 8 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/winforms/.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /obj 3 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/winforms/Demo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | WinExe 6 | net6.0-windows 7 | enable 8 | true 9 | enable 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/winforms/README.md: -------------------------------------------------------------------------------- 1 | # Demo 2 | 3 | Run the app: 4 | 5 | $ ./run.sh 6 | -------------------------------------------------------------------------------- /elixirkit/demo/rel/winforms/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | dotnet build 5 | target_dir="$PWD/bin/Debug/net6.0-windows" 6 | (cd ../.. && mix release --overwrite --path=${target_dir}/rel) 7 | dotnet run --no-build 8 | -------------------------------------------------------------------------------- /elixirkit/demo/test/demo_test.exs: -------------------------------------------------------------------------------- 1 | defmodule DemoTest do 2 | use ExUnit.Case, async: true 3 | end 4 | -------------------------------------------------------------------------------- /elixirkit/demo/test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /elixirkit/elixirkit_dotnet/.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /obj 3 | -------------------------------------------------------------------------------- /elixirkit/elixirkit_dotnet/ElixirKit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net46 5 | 10.0 6 | enable 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /elixirkit/elixirkit_swift/.gitignore: -------------------------------------------------------------------------------- 1 | /.build 2 | /Packages 3 | /*.xcodeproj 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/config/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /elixirkit/elixirkit_swift/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.5 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "ElixirKit", 7 | platforms: [ 8 | .macOS(.v11) 9 | ], 10 | products: [ 11 | .library( 12 | name: "ElixirKit", 13 | targets: ["ElixirKit"] 14 | ), 15 | ], 16 | dependencies: [], 17 | targets: [ 18 | .target( 19 | name: "ElixirKit", 20 | dependencies: [] 21 | ), 22 | .testTarget( 23 | name: "ElixirKitTests", 24 | dependencies: ["ElixirKit"] 25 | ) 26 | ] 27 | ) 28 | -------------------------------------------------------------------------------- /elixirkit/elixirkit_swift/README.md: -------------------------------------------------------------------------------- 1 | # ElixirKit 2 | -------------------------------------------------------------------------------- /elixirkit/elixirkit_swift/Tests/ElixirKitTests/ElixirKitTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import ElixirKit 3 | 4 | final class ElixirKitTests: XCTestCase { 5 | func testExample() throws { 6 | XCTAssertEqual(1 + 2, 3) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /elixirkit/lib/elixirkit.ex: -------------------------------------------------------------------------------- 1 | defmodule ElixirKit do 2 | def start do 3 | Supervisor.start_child(ElixirKit.Supervisor, {ElixirKit.Server, self()}) 4 | end 5 | 6 | def publish(name, data) do 7 | GenServer.call(ElixirKit.Server, {:send_event, name, data}) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /elixirkit/lib/elixirkit/application.ex: -------------------------------------------------------------------------------- 1 | defmodule ElixirKit.Application do 2 | @moduledoc false 3 | 4 | use Application 5 | 6 | @impl true 7 | def start(_type, _args) do 8 | children = [] 9 | opts = [strategy: :one_for_one, name: ElixirKit.Supervisor] 10 | Supervisor.start_link(children, opts) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /elixirkit/lib/elixirkit/server.ex: -------------------------------------------------------------------------------- 1 | defmodule ElixirKit.Server do 2 | @moduledoc false 3 | 4 | use GenServer 5 | 6 | def start_link(arg) do 7 | GenServer.start_link(__MODULE__, arg, name: __MODULE__) 8 | end 9 | 10 | @impl true 11 | def init(pid) do 12 | port = System.fetch_env!("ELIXIRKIT_PORT") |> String.to_integer() 13 | {:ok, socket} = :gen_tcp.connect(~c"localhost", port, mode: :binary, packet: 4) 14 | {:ok, %{pid: pid, socket: socket}} 15 | end 16 | 17 | @impl true 18 | def handle_call({:send_event, name, data}, _from, state) do 19 | payload = [name, ?:, data] 20 | :ok = :gen_tcp.send(state.socket, payload) 21 | {:reply, :ok, state} 22 | end 23 | 24 | @impl true 25 | def handle_info({:tcp, socket, payload}, state) when socket == state.socket do 26 | [name, data] = :binary.split(payload, ":") 27 | send(state.pid, {:event, name, data}) 28 | {:noreply, state} 29 | end 30 | 31 | @impl true 32 | def handle_info({:tcp_closed, socket}, state) when socket == state.socket do 33 | {:stop, :shutdown, state} 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /elixirkit/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule ElixirKit.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :elixirkit, 7 | version: "0.1.0", 8 | elixir: "~> 1.14", 9 | start_permanent: Mix.env() == :prod, 10 | package: package(), 11 | deps: deps() 12 | ] 13 | end 14 | 15 | def application do 16 | [ 17 | extra_applications: [:logger], 18 | mod: {ElixirKit.Application, []} 19 | ] 20 | end 21 | 22 | def package do 23 | [ 24 | files: [ 25 | "lib", 26 | "elixirkit_swift/Package.swift", 27 | "elixirkit_swift/Sources", 28 | "elixirkit_dotnet/ElixirKit.csproj", 29 | "elixirkit_dotnet/ElixirKit.cs", 30 | "mix.exs", 31 | "README.md" 32 | ] 33 | ] 34 | end 35 | 36 | defp deps do 37 | [] 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /elixirkit/otp_bootstrap/.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | -------------------------------------------------------------------------------- /elixirkit/test/elixirkit_test.exs: -------------------------------------------------------------------------------- 1 | defmodule ElixirKitTest do 2 | use ExUnit.Case, async: true 3 | end 4 | -------------------------------------------------------------------------------- /elixirkit/test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /iframe/.formatter.exs: -------------------------------------------------------------------------------- 1 | [ 2 | import_deps: [:plug], 3 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 4 | ] 5 | -------------------------------------------------------------------------------- /iframe/.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where third-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | livebook_space-*.tar 24 | 25 | # Temporary files, for example, from tests. 26 | /tmp/ 27 | -------------------------------------------------------------------------------- /iframe/Dockerfile: -------------------------------------------------------------------------------- 1 | # Stage 1: build 2 | FROM hexpm/elixir:1.13.2-erlang-24.1.7-alpine-3.15.0 AS build 3 | 4 | # Install build dependencies 5 | RUN apk add --no-cache build-base git 6 | 7 | WORKDIR /app 8 | 9 | # Install hex and rebar 10 | RUN mix local.hex --force && \ 11 | mix local.rebar --force 12 | 13 | # Set build ENV 14 | ENV MIX_ENV=prod 15 | 16 | # Install mix dependencies 17 | COPY mix.exs mix.lock ./ 18 | RUN mix do deps.get, deps.compile 19 | 20 | # Compile and build release 21 | COPY priv priv 22 | COPY lib lib 23 | RUN mix do compile, release 24 | 25 | # Stage 2: release image 26 | FROM alpine:3.15.0 27 | 28 | # Install runtime dependencies 29 | RUN apk add --no-cache openssl ncurses-libs libstdc++ 30 | 31 | WORKDIR /app 32 | 33 | # Copy the release build from the previous stage. 34 | COPY --from=build /app/_build/prod/rel/livebook_space ./ 35 | 36 | ENV HOME=/app 37 | 38 | CMD [ "/app/bin/livebook_space", "start" ] 39 | -------------------------------------------------------------------------------- /iframe/README.md: -------------------------------------------------------------------------------- 1 | # Livebook iframes 2 | 3 | Livebook executes custom JavaScript inside iframes. When running on http, 4 | they are served on a separate port. For https, they are safely served by 5 | [livebookusercontent.com](https://livebookusercontent.com), which runs this application. 6 | -------------------------------------------------------------------------------- /iframe/fly.toml: -------------------------------------------------------------------------------- 1 | app = "livebook-space" 2 | 3 | kill_signal = "SIGTERM" 4 | kill_timeout = 5 5 | 6 | [env] 7 | 8 | [[services]] 9 | internal_port = 4000 10 | protocol = "tcp" 11 | 12 | [[services.ports]] 13 | handlers = ["http"] 14 | port = 80 15 | 16 | [[services.ports]] 17 | handlers = ["tls", "http"] 18 | port = 443 19 | 20 | [[services.tcp_checks]] 21 | grace_period = "30s" 22 | interval = "15s" 23 | restart_limit = 6 24 | timeout = "2s" 25 | -------------------------------------------------------------------------------- /iframe/lib/livebook_space/application.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookSpace.Application do 2 | use Application 3 | 4 | def start(_type, _args) do 5 | children = [ 6 | {Bandit, scheme: :http, plug: LivebookSpaceWeb.Plug, port: 4000} 7 | ] 8 | 9 | opts = [strategy: :one_for_one, name: LivebookSpace.Supervisor] 10 | Supervisor.start_link(children, opts) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /iframe/lib/livebook_space_web/plug.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookSpaceWeb.Plug do 2 | use Plug.Builder 3 | 4 | plug Plug.Static, 5 | from: {:livebook_space, "priv/static/iframe"}, 6 | at: "/iframe", 7 | # Iframes are versioned, so we cache them for long 8 | cache_control_for_etags: "public, max-age=31536000", 9 | headers: [ 10 | # Enable CORS to allow Livebook fetch the content and verify its integrity 11 | {"access-control-allow-origin", "*"}, 12 | {"content-type", "text/html; charset=utf-8"} 13 | ] 14 | 15 | plug Plug.Static, from: :livebook_space, at: "/" 16 | 17 | plug :not_found 18 | 19 | defp not_found(%{path_info: [], method: "GET"} = conn, _) do 20 | call(%{conn | path_info: ["index.html"], request_path: "/index.html"}, []) 21 | end 22 | 23 | defp not_found(conn, _) do 24 | send_resp(conn, 404, "not found") 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /iframe/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule LivebookSpace.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :livebook_space, 7 | version: "0.1.0", 8 | elixir: "~> 1.13", 9 | start_permanent: Mix.env() == :prod, 10 | deps: deps() 11 | ] 12 | end 13 | 14 | def application do 15 | [ 16 | extra_applications: [:logger], 17 | mod: {LivebookSpace.Application, []} 18 | ] 19 | end 20 | 21 | defp deps do 22 | [ 23 | {:bandit, "~> 1.0"} 24 | ] 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /iframe/priv/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/iframe/priv/static/images/logo.png -------------------------------------------------------------------------------- /iframe/priv/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / 3 | -------------------------------------------------------------------------------- /lib/livebook/apps/deployment_supervisor.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Apps.DeploymentSupervisor do 2 | # Supervision tree related to orchestrating app deployments in the 3 | # cluster. 4 | 5 | use Supervisor 6 | 7 | @name __MODULE__ 8 | 9 | @doc """ 10 | Starts the supervisor. 11 | """ 12 | @spec start_link(keyword()) :: GenServer.on_start() 13 | def start_link(_opts) do 14 | Supervisor.start_link(__MODULE__, {}, name: @name) 15 | end 16 | 17 | @doc """ 18 | Starts a node-local instance of `Livebook.Apps.Manager`. 19 | """ 20 | @spec start_manager() :: Supervisor.on_start_child() 21 | def start_manager() do 22 | Supervisor.start_child(@name, Livebook.Apps.Manager) 23 | end 24 | 25 | @impl true 26 | def init({}) do 27 | children = [ 28 | # Start the supervisor dynamically managing apps 29 | {DynamicSupervisor, name: Livebook.AppSupervisor, strategy: :one_for_one}, 30 | # Process group for app deployers 31 | %{id: Livebook.Apps.Deployer.PG, start: {:pg, :start_link, [Livebook.Apps.Deployer.PG]}}, 32 | # Node-local app deployer 33 | Livebook.Apps.Deployer, 34 | # Node-local app manager watcher 35 | Livebook.Apps.ManagerWatcher 36 | ] 37 | 38 | Supervisor.init(children, strategy: :one_for_one) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/livebook/hubs/metadata.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Hubs.Metadata do 2 | defstruct [:id, :name, :provider, :emoji, connected?: false] 3 | 4 | @type t :: %__MODULE__{ 5 | id: String.t(), 6 | name: String.t(), 7 | provider: struct(), 8 | emoji: String.t(), 9 | connected?: boolean() 10 | } 11 | end 12 | -------------------------------------------------------------------------------- /lib/livebook/notebook/cell/markdown.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Notebook.Cell.Markdown do 2 | # Notebook cell with Markdown text content. 3 | # 4 | # It consists of text content that the user can edit and which is 5 | # rendered on the page. 6 | 7 | defstruct [:id, :source] 8 | 9 | alias Livebook.Utils 10 | alias Livebook.Notebook.Cell 11 | 12 | @type t :: %__MODULE__{ 13 | id: Cell.id(), 14 | source: String.t() | :__pruned__ 15 | } 16 | 17 | @doc """ 18 | Returns an empty cell. 19 | """ 20 | @spec new() :: t() 21 | def new() do 22 | %__MODULE__{ 23 | id: Utils.random_id(), 24 | source: "" 25 | } 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/livebook/notebook/learn/files/portal-drop.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/lib/livebook/notebook/learn/files/portal-drop.jpeg -------------------------------------------------------------------------------- /lib/livebook/notebook/learn/files/portal-list.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/lib/livebook/notebook/learn/files/portal-list.jpeg -------------------------------------------------------------------------------- /lib/livebook/notebook/section.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Notebook.Section do 2 | # Data structure representing a single section in a notebook. 3 | # 4 | # Section can contains a number of cells and serves as a way of 5 | # grouping related cells. 6 | # 7 | # A section may optionally have a parent, in which case it is a 8 | # **branching section**. Such section logically follows its parent 9 | # section and has no impact on any further sections. 10 | 11 | defstruct [:id, :name, :cells, :parent_id] 12 | 13 | alias Livebook.Notebook.Cell 14 | alias Livebook.Utils 15 | 16 | @type id :: Utils.id() 17 | 18 | @type t :: %__MODULE__{ 19 | id: id(), 20 | name: String.t(), 21 | cells: list(Cell.t()), 22 | parent_id: id() | nil 23 | } 24 | 25 | @doc """ 26 | Returns a blank section. 27 | """ 28 | @spec new() :: t() 29 | def new() do 30 | %__MODULE__{ 31 | id: Utils.random_id(), 32 | name: "Section", 33 | cells: [], 34 | parent_id: nil 35 | } 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/livebook/runtime/erl_dist/evaluator_supervisor.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Runtime.ErlDist.EvaluatorSupervisor do 2 | # Supervisor responsible for dynamically spawning 3 | # and terminating evaluator server processes. 4 | 5 | use DynamicSupervisor 6 | 7 | alias Livebook.Runtime.Evaluator 8 | 9 | def start_link(opts \\ []) do 10 | DynamicSupervisor.start_link(__MODULE__, opts) 11 | end 12 | 13 | @impl true 14 | def init(_opts) do 15 | DynamicSupervisor.init(strategy: :one_for_one) 16 | end 17 | 18 | @doc """ 19 | Spawns a new evaluator. 20 | """ 21 | @spec start_evaluator(pid(), keyword()) :: {:ok, Evaluator.t()} | {:error, any()} 22 | def start_evaluator(supervisor, opts) do 23 | case DynamicSupervisor.start_child(supervisor, {Evaluator, opts}) do 24 | {:ok, _pid, evaluator} -> {:ok, evaluator} 25 | {:error, reason} -> {:error, reason} 26 | end 27 | end 28 | 29 | @doc """ 30 | Terminates the given evaluator. 31 | """ 32 | @spec terminate_evaluator(pid(), Evaluator.t()) :: :ok 33 | def terminate_evaluator(supervisor, evaluator) do 34 | DynamicSupervisor.terminate_child(supervisor, evaluator.pid) 35 | :ok 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/livebook/runtime/erl_dist/smart_cell_gl.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Runtime.ErlDist.SmartCellGL do 2 | use GenServer 3 | 4 | @spec start_link(pid()) :: GenServer.on_start() 5 | def start_link(runtime_broadcast_to) do 6 | GenServer.start_link(__MODULE__, {runtime_broadcast_to}) 7 | end 8 | 9 | @impl true 10 | def init({runtime_broadcast_to}) do 11 | {:ok, %{runtime_broadcast_to: runtime_broadcast_to}} 12 | end 13 | 14 | @impl true 15 | def handle_info({:io_request, from, reply_as, req}, state) do 16 | case io_request(req, state) do 17 | :forward -> 18 | # Forward the request to own group leader 19 | gl = Process.group_leader() 20 | send(gl, {:io_request, from, reply_as, req}) 21 | 22 | {:reply, reply} -> 23 | send(from, {:io_reply, reply_as, reply}) 24 | end 25 | 26 | {:noreply, state} 27 | end 28 | 29 | defp io_request(:livebook_get_broadcast_target, state) do 30 | {:reply, {:ok, state.runtime_broadcast_to}} 31 | end 32 | 33 | defp io_request(_req, _state) do 34 | :forward 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/livebook/secrets.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Secrets do 2 | # Shared secret functionality across all hubs. 3 | 4 | alias Livebook.Secrets.Secret 5 | 6 | @doc """ 7 | Returns an `%Ecto.Changeset{}` for tracking secret changes. 8 | """ 9 | @spec change_secret(Secret.t(), map()) :: Ecto.Changeset.t() 10 | def change_secret(%Secret{} = secret, attrs) do 11 | Secret.changeset(secret, attrs) 12 | end 13 | 14 | @doc """ 15 | Updates secret with the given changes. 16 | """ 17 | @spec update_secret(Secret.t(), map()) :: {:ok, Secret.t()} | {:error, Ecto.Changeset.t()} 18 | def update_secret(%Secret{} = secret, attrs) do 19 | secret 20 | |> Secret.changeset(attrs) 21 | |> Ecto.Changeset.apply_action(:update) 22 | end 23 | 24 | @secret_startup_key :livebook_startup_secrets 25 | 26 | @doc """ 27 | Get the startup secrets list from persistent term. 28 | """ 29 | @spec get_startup_secrets() :: list(Secret.t()) 30 | def get_startup_secrets do 31 | :persistent_term.get(@secret_startup_key, []) 32 | end 33 | 34 | @doc """ 35 | Sets additional secrets that are kept only in memory. 36 | """ 37 | @spec set_startup_secrets(list(Secret.t())) :: :ok 38 | def set_startup_secrets(secrets) do 39 | :persistent_term.put(@secret_startup_key, secrets) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/livebook/secrets/secret.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Secrets.Secret do 2 | use Ecto.Schema 3 | import Ecto.Changeset 4 | 5 | @type t :: %__MODULE__{ 6 | name: String.t() | nil, 7 | value: String.t() | nil, 8 | hub_id: String.t() | nil, 9 | deployment_group_id: String.t() | nil 10 | } 11 | 12 | @primary_key {:name, :string, autogenerate: false} 13 | embedded_schema do 14 | field :value, :string 15 | field :hub_id, :string 16 | field :deployment_group_id, :string 17 | end 18 | 19 | def changeset(secret, attrs \\ %{}) do 20 | secret 21 | |> cast(attrs, [:name, :value, :hub_id, :deployment_group_id]) 22 | |> update_change(:name, &String.upcase/1) 23 | |> validate_format(:name, ~r/^\w+$/, 24 | message: "should contain only alphanumeric characters and underscore" 25 | ) 26 | |> validate_required([:name, :value]) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/livebook/session/worker.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Session.Worker do 2 | # A dedicated process for offloading the session process, 3 | # when the session state is not necessary. 4 | # 5 | # In particular, this process handles broadcast messages 6 | # sent from within the runtime and distributes them to the 7 | # actual subscribers via pubsub. 8 | 9 | use GenServer 10 | 11 | def start_link(session_id) do 12 | GenServer.start_link(__MODULE__, {session_id}) 13 | end 14 | 15 | @impl true 16 | def init({session_id}) do 17 | {:ok, %{session_id: session_id}} 18 | end 19 | 20 | @impl true 21 | def handle_info({:runtime_broadcast, topic, subtopic, message}, state) do 22 | Livebook.Session.broadcast_runtime_event(state.session_id, topic, subtopic, message) 23 | {:noreply, state} 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/livebook/settings/env_var.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Settings.EnvVar do 2 | use Ecto.Schema 3 | import Ecto.Changeset 4 | 5 | @type t :: %__MODULE__{ 6 | name: String.t() | nil, 7 | value: String.t() | nil 8 | } 9 | 10 | @primary_key {:name, :string, autogenerate: false} 11 | embedded_schema do 12 | field :value, :string 13 | end 14 | 15 | def changeset(env_var, attrs \\ %{}) do 16 | env_var 17 | |> cast(attrs, [:name, :value]) 18 | |> update_change(:name, &String.upcase/1) 19 | |> validate_format(:name, ~r/^(?!LB_)\w+$/, message: "cannot start with the LB_ prefix") 20 | |> validate_required([:name, :value]) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/livebook/teams/agent.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Teams.Agent do 2 | use Ecto.Schema 3 | 4 | @type t :: %__MODULE__{ 5 | id: String.t() | nil, 6 | name: String.t() | nil, 7 | hub_id: String.t() | nil, 8 | org_id: String.t() | nil, 9 | deployment_group_id: String.t() | nil 10 | } 11 | 12 | @primary_key {:id, :string, autogenerate: false} 13 | embedded_schema do 14 | field :name, :string 15 | field :hub_id, :string 16 | field :org_id, :string 17 | field :deployment_group_id, :string 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/livebook/teams/agent_key.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Teams.AgentKey do 2 | use Ecto.Schema 3 | 4 | @type t :: %__MODULE__{ 5 | id: String.t() | nil, 6 | key: String.t() | nil, 7 | deployment_group_id: String.t() | nil 8 | } 9 | 10 | @primary_key {:id, :string, autogenerate: false} 11 | embedded_schema do 12 | field :key, :string 13 | field :deployment_group_id, :string 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/livebook/teams/authorization_group.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Teams.AuthorizationGroup do 2 | use Ecto.Schema 3 | 4 | @type t :: %__MODULE__{ 5 | provider_id: String.t() | nil, 6 | group_name: String.t() | nil 7 | } 8 | 9 | @primary_key false 10 | embedded_schema do 11 | field :provider_id, :string 12 | field :group_name, :string 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/livebook/teams/environment_variable.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Teams.EnvironmentVariable do 2 | use Ecto.Schema 3 | 4 | @type t :: %__MODULE__{ 5 | name: String.t(), 6 | value: String.t(), 7 | hub_id: String.t(), 8 | deployment_group_id: String.t() 9 | } 10 | 11 | @enforce_keys [:name, :value, :hub_id, :deployment_group_id] 12 | 13 | @primary_key false 14 | embedded_schema do 15 | field :name, :string 16 | field :value, :string 17 | field :hub_id, :string 18 | field :deployment_group_id, :string 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/livebook/utils/supervision_step.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Utils.SupervisionStep do 2 | use GenServer, restart: :temporary 3 | 4 | @doc """ 5 | Synchronously runs the given function as part of supervision tree 6 | startup. 7 | """ 8 | @spec start_link({atom(), function()}) :: :ignore 9 | def start_link({_id, fun}) do 10 | GenServer.start_link(__MODULE__, fun) 11 | end 12 | 13 | @doc false 14 | def child_spec({id, fun}) do 15 | child_spec = super({id, fun}) 16 | update_in(child_spec.id, &{&1, id}) 17 | end 18 | 19 | @impl true 20 | def init(fun) do 21 | fun.() 22 | :ignore 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/livebook/zta/basic_auth.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.ZTA.BasicAuth do 2 | @behaviour Livebook.ZTA 3 | 4 | @impl true 5 | def child_spec(opts) do 6 | %{id: __MODULE__, start: {__MODULE__, :start_link, [opts]}} 7 | end 8 | 9 | def start_link(options) do 10 | name = Keyword.fetch!(options, :name) 11 | identity_key = Keyword.fetch!(options, :identity_key) 12 | [username, password] = String.split(identity_key, ":", parts: 2) 13 | 14 | Livebook.ZTA.put(name, {username, password}) 15 | :ignore 16 | end 17 | 18 | @impl true 19 | def authenticate(name, conn, _opts) do 20 | {username, password} = Livebook.ZTA.get(name) 21 | conn = Plug.BasicAuth.basic_auth(conn, username: username, password: password) 22 | 23 | if conn.halted do 24 | {conn, nil} 25 | else 26 | {conn, %{}} 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/livebook/zta/pass_through.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.ZTA.PassThrough do 2 | @behaviour Livebook.ZTA 3 | 4 | @impl true 5 | def child_spec(_opts) do 6 | %{id: __MODULE__, start: {Function, :identity, [:ignore]}} 7 | end 8 | 9 | @impl true 10 | def authenticate(_name, conn, _opts) do 11 | {conn, %{}} 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/livebook_app.ex: -------------------------------------------------------------------------------- 1 | if Mix.target() == :app do 2 | defmodule LivebookApp do 3 | use GenServer 4 | 5 | def start_link(arg) do 6 | GenServer.start_link(__MODULE__, arg, name: __MODULE__) 7 | end 8 | 9 | @impl true 10 | def init(_) do 11 | {:ok, pid} = ElixirKit.start() 12 | ref = Process.monitor(pid) 13 | 14 | ElixirKit.publish("url", LivebookWeb.Endpoint.access_url()) 15 | 16 | {:ok, %{ref: ref}} 17 | end 18 | 19 | @impl true 20 | def handle_info({:event, "open", url}, state) do 21 | url 22 | |> Livebook.Utils.expand_desktop_url() 23 | |> Livebook.Utils.browser_open() 24 | 25 | {:noreply, state} 26 | end 27 | 28 | @impl true 29 | def handle_info({:DOWN, ref, :process, _, :shutdown}, state) when ref == state.ref do 30 | Livebook.Config.shutdown() 31 | {:noreply, state} 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/livebook_cli/task.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookCLI.Task do 2 | @doc """ 3 | Returns a description of the task usage. 4 | """ 5 | @callback usage() :: String.t() 6 | 7 | @doc """ 8 | Runs the task with the given list of command line arguments. 9 | """ 10 | @callback call(args :: list(String.t())) :: :ok 11 | end 12 | -------------------------------------------------------------------------------- /lib/livebook_web/channels/socket.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.Socket do 2 | use Phoenix.Socket 3 | 4 | channel "js_view", LivebookWeb.JSViewChannel 5 | 6 | @impl true 7 | def connect(_params, socket, info) do 8 | # The session is present only if the CSRF token is valid. We rely 9 | # on CSRF token, because we don't check connection origin as noted 10 | # in LivebookWeb.Endpoint. 11 | if info.session do 12 | {:ok, socket} 13 | else 14 | :error 15 | end 16 | end 17 | 18 | @impl true 19 | def id(_socket), do: nil 20 | end 21 | -------------------------------------------------------------------------------- /lib/livebook_web/components/file_system_components.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.FileSystemComponents do 2 | use LivebookWeb, :html 3 | 4 | alias Livebook.FileSystem 5 | 6 | @doc """ 7 | Formats the given file system into a short name. 8 | """ 9 | def file_system_name(file_system_module) 10 | 11 | def file_system_name(FileSystem.Local), do: "Disk" 12 | def file_system_name(FileSystem.S3), do: "S3" 13 | 14 | @doc """ 15 | Formats the given file system into a descriptive label. 16 | """ 17 | def file_system_label(file_system) 18 | 19 | def file_system_label(%FileSystem.Local{}), do: "Disk" 20 | def file_system_label(%FileSystem.S3{} = fs), do: fs.bucket_url 21 | 22 | @doc """ 23 | Renders an icon representing the given file system. 24 | """ 25 | def file_system_icon(assigns) 26 | 27 | def file_system_icon(%{file_system: %FileSystem.Local{}} = assigns) do 28 | ~H""" 29 | <.remix_icon icon="hard-drive-2-line leading-none" /> 30 | """ 31 | end 32 | 33 | def file_system_icon(%{file_system: %FileSystem.S3{}} = assigns) do 34 | ~H""" 35 | 36 | S3 37 | 38 | """ 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/livebook_web/components/layouts.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.Layouts do 2 | use LivebookWeb, :html 3 | 4 | embed_templates "layouts/*" 5 | end 6 | -------------------------------------------------------------------------------- /lib/livebook_web/components/layouts/app.html.heex: -------------------------------------------------------------------------------- 1 |
2 | {@inner_content} 3 |
4 | -------------------------------------------------------------------------------- /lib/livebook_web/components/layouts/live.html.heex: -------------------------------------------------------------------------------- 1 |
2 | <.flash_group flash={@flash} /> 3 | {@inner_content} 4 |
5 | 6 | <.confirm_root confirm_state={@confirm_state} /> 7 | -------------------------------------------------------------------------------- /lib/livebook_web/controllers/auth_html.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.AuthHTML do 2 | use LivebookWeb, :html 3 | 4 | embed_templates "auth_html/*" 5 | end 6 | -------------------------------------------------------------------------------- /lib/livebook_web/controllers/error_json.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.ErrorJSON do 2 | # By default, Phoenix returns the status message from 3 | # the template name. For example, "404.json" becomes 4 | # "Not Found". 5 | def render(template, _assigns) do 6 | %{errors: %{detail: Phoenix.Controller.status_message_from_template(template)}} 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/livebook_web/controllers/health_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.HealthController do 2 | use LivebookWeb, :controller 3 | 4 | def index(conn, _params) do 5 | conn 6 | |> put_resp_header("access-control-allow-origin", "*") 7 | |> json(%{ 8 | "application" => "livebook" 9 | }) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/livebook_web/controllers/user_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.UserController do 2 | use LivebookWeb, :controller 3 | 4 | def logout(conn, _params) do 5 | if get_session(conn, :user_id) do 6 | if Livebook.Config.logout_enabled?() do 7 | do_zta_logout(conn) 8 | else 9 | do_logout(conn) 10 | end 11 | else 12 | redirect(conn, to: ~p"/") 13 | end 14 | end 15 | 16 | defp do_logout(conn) do 17 | conn 18 | |> configure_session(renew: true) 19 | |> clear_session() 20 | |> redirect(to: ~p"/") 21 | end 22 | 23 | defp do_zta_logout(conn) do 24 | {_type, module, _key} = Livebook.Config.identity_provider() 25 | module.logout(LivebookWeb.ZTA, conn) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/livebook_web/controllers/user_html.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.UserHTML do 2 | use LivebookWeb, :html 3 | 4 | embed_templates "user_html/*" 5 | end 6 | -------------------------------------------------------------------------------- /lib/livebook_web/errors.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.NotFoundError do 2 | defexception [:message, plug_status: 404] 3 | end 4 | 5 | defmodule LivebookWeb.BadRequestError do 6 | defexception [:message, plug_status: 400] 7 | end 8 | -------------------------------------------------------------------------------- /lib/livebook_web/live/app_session_live/cell_outputs.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.AppSessionLive.CellOutputsComponent do 2 | use LivebookWeb, :live_component 3 | 4 | @impl true 5 | def mount(socket) do 6 | {:ok, stream(socket, :outputs, [])} 7 | end 8 | 9 | @impl true 10 | def update(assigns, socket) do 11 | socket = assign(socket, assigns) 12 | 13 | stream_items = 14 | for {idx, output} <- Enum.reverse(assigns.cell_view.outputs) do 15 | %{id: Integer.to_string(idx), idx: idx, output: output} 16 | end 17 | 18 | socket = stream(socket, :outputs, stream_items) 19 | 20 | {:ok, socket} 21 | end 22 | 23 | @impl true 24 | def render(assigns) do 25 | ~H""" 26 |
41 | """ 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/livebook_web/live/hooks/auth_hook.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.AuthHook do 2 | import Phoenix.LiveView 3 | 4 | use LivebookWeb, :verified_routes 5 | 6 | def on_mount(:default, _params, session, socket) do 7 | uri = get_connect_info(socket, :uri) 8 | socket = attach_hook(socket, :authorization_subscription, :handle_info, &handle_info/2) 9 | 10 | if LivebookWeb.AuthPlug.authorized?(session || %{}, uri.port) do 11 | {:cont, socket} 12 | else 13 | {:halt, redirect(socket, to: ~p"/")} 14 | end 15 | end 16 | 17 | defp handle_info({:server_authorization_updated, %{hub_id: hub_id}}, socket) do 18 | # We already updated the current user, so we just need to force the redirection. 19 | # But, for apps, we redirect directly from the app session 20 | current_user = socket.assigns.current_user 21 | 22 | if current_user.payload do 23 | if current_user.access_type == :full and 24 | Livebook.Hubs.TeamClient.user_full_access?(hub_id, current_user.groups) do 25 | {:halt, socket} 26 | else 27 | {:halt, redirect(socket, to: ~p"/")} 28 | end 29 | else 30 | {:halt, socket} 31 | end 32 | end 33 | 34 | defp handle_info(_message, socket), do: {:cont, socket} 35 | end 36 | -------------------------------------------------------------------------------- /lib/livebook_web/live/output/image_component.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.Output.ImageComponent do 2 | use LivebookWeb, :live_component 3 | 4 | @impl true 5 | def render(assigns) do 6 | ~H""" 7 | output image 14 | """ 15 | end 16 | 17 | defp data_url(content, mime_type) do 18 | image_base64 = Base.encode64(content) 19 | ["data:", mime_type, ";base64,", image_base64] 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/livebook_web/live/session_live/insert_file_component.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.SessionLive.InsertFileComponent do 2 | use LivebookWeb, :live_component 3 | 4 | @impl true 5 | def render(assigns) do 6 | ~H""" 7 |
8 |

9 | Suggested actions 10 |

11 |

12 | What do you want to do with the file? 13 |

14 |
15 |
20 | 21 | {handler.definition.description} 22 | 23 |
24 |
25 |
26 | """ 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/livebook_web/plugs/configured_plug.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.ConfiguredPlug do 2 | # Runs plugs configured for the :livebook application 3 | 4 | @behaviour Plug 5 | 6 | @impl true 7 | def init(opts), do: opts 8 | 9 | @impl true 10 | def call(conn, _opts) do 11 | case Application.fetch_env!(:livebook, :plugs) do 12 | [] -> conn 13 | plugs -> Plug.run(conn, plugs) 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /proto/.formatter.exs: -------------------------------------------------------------------------------- 1 | # Used by "mix format" 2 | [ 3 | import_deps: [:protobuf], 4 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 5 | ] 6 | -------------------------------------------------------------------------------- /proto/.gitignore: -------------------------------------------------------------------------------- 1 | # The directory Mix will write compiled artifacts to. 2 | /_build/ 3 | 4 | # If you run "mix test --cover", coverage assets end up here. 5 | /cover/ 6 | 7 | # The directory Mix downloads your dependencies sources to. 8 | /deps/ 9 | 10 | # Where third-party dependencies like ExDoc output generated docs. 11 | /doc/ 12 | 13 | # Ignore .fetch files in case you like to edit your project deps locally. 14 | /.fetch 15 | 16 | # If the VM crashes, it generates a dump, let's ignore it too. 17 | erl_crash.dump 18 | 19 | # Also ignore archive artifacts (built via "mix archive.build"). 20 | *.ez 21 | 22 | # Ignore package tarball (built via "mix hex.build"). 23 | livebook_proto-*.tar 24 | 25 | # Temporary files, for example, from tests. 26 | /tmp/ 27 | -------------------------------------------------------------------------------- /proto/README.md: -------------------------------------------------------------------------------- 1 | # Livebook Protobuf 2 | 3 | Livebook uses Protobuf messages for cross-backend communication over WebSocket. 4 | 5 | ## Generate message 6 | 7 | To generate the message modules, you'll need to run: 8 | 9 | ```sh 10 | mix protobuf.generate 11 | ``` 12 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/agent.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.Agent do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | field :name, 2, type: :string 6 | field :org_id, 3, type: :string, json_name: "orgId" 7 | field :deployment_group_id, 4, type: :string, json_name: "deploymentGroupId" 8 | end 9 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/agent_connected.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.AgentConnected do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :name, 2, type: :string 5 | field :public_key, 3, type: :string, json_name: "publicKey" 6 | field :deployment_group_id, 4, type: :int32, json_name: "deploymentGroupId" 7 | field :secrets, 5, repeated: true, type: LivebookProto.Secret 8 | field :file_systems, 6, repeated: true, type: LivebookProto.FileSystem, json_name: "fileSystems" 9 | 10 | field :deployment_groups, 7, 11 | repeated: true, 12 | type: LivebookProto.DeploymentGroup, 13 | json_name: "deploymentGroups" 14 | 15 | field :app_deployments, 8, 16 | repeated: true, 17 | type: LivebookProto.AppDeployment, 18 | json_name: "appDeployments" 19 | 20 | field :agents, 9, repeated: true, type: LivebookProto.Agent 21 | field :billing_status, 10, type: LivebookProto.BillingStatus, json_name: "billingStatus" 22 | end 23 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/agent_joined.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.AgentJoined do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :agent, 1, type: LivebookProto.Agent 5 | end 6 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/agent_key.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.AgentKey do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | field :key, 2, type: :string 6 | field :deployment_group_id, 3, type: :string, json_name: "deploymentGroupId" 7 | end 8 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/agent_left.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.AgentLeft do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | end 6 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/app_deployment.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.AppDeployment do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | field :title, 2, type: :string 6 | field :sha, 3, type: :string 7 | field :revision_id, 4, type: :string, json_name: "revisionId" 8 | field :slug, 5, type: :string 9 | field :deployment_group_id, 6, type: :string, json_name: "deploymentGroupId" 10 | field :deployed_by, 7, type: :string, json_name: "deployedBy" 11 | field :deployed_at, 8, type: :int64, json_name: "deployedAt" 12 | field :multi_session, 9, type: :bool, json_name: "multiSession" 13 | field :access_type, 10, type: :string, json_name: "accessType" 14 | field :version, 11, type: :string 15 | 16 | field :authorization_groups, 12, 17 | repeated: true, 18 | type: LivebookProto.AuthorizationGroup, 19 | json_name: "authorizationGroups" 20 | end 21 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/app_deployment_started.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.AppDeploymentStarted do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :app_deployment, 1, type: LivebookProto.AppDeployment, json_name: "appDeployment" 5 | end 6 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/app_deployment_status.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.AppDeploymentStatus do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | field :deployment_group_id, 2, type: :string, json_name: "deploymentGroupId" 6 | field :version, 3, type: :string 7 | field :status, 4, type: LivebookProto.AppDeploymentStatusType, enum: true 8 | end 9 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/app_deployment_status_report.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.AppDeploymentStatusReport do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :app_deployment_statuses, 1, 5 | repeated: true, 6 | type: LivebookProto.AppDeploymentStatus, 7 | json_name: "appDeploymentStatuses" 8 | end 9 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/app_deployment_status_type.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.AppDeploymentStatusType do 2 | @moduledoc """ 3 | We're only using Enum in this case because 4 | the Client is the source of the information, 5 | and the Server will always be up-to-date. 6 | 7 | Otherwise, it shouldn't be used. 8 | """ 9 | 10 | use Protobuf, enum: true, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 11 | 12 | field :preparing, 0 13 | field :available, 1 14 | end 15 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/app_deployment_stopped.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.AppDeploymentStopped do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | end 6 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/app_deployment_updated.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.AppDeploymentUpdated do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :app_deployment, 1, type: LivebookProto.AppDeployment, json_name: "appDeployment" 5 | end 6 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/authorization_group.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.AuthorizationGroup do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :provider_id, 1, type: :string, json_name: "providerId" 5 | field :group_name, 2, type: :string, json_name: "groupName" 6 | end 7 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/billing_status.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.BillingStatus do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | oneof :type, 0 5 | 6 | field :disabled, 1, type: :bool 7 | field :trialing, 2, type: LivebookProto.BillingStatusTrialing, oneof: 0 8 | 9 | field :trial_ended, 3, 10 | type: LivebookProto.BillingStatusTrialEnded, 11 | json_name: "trialEnded", 12 | oneof: 0 13 | 14 | field :canceling, 4, type: LivebookProto.BillingStatusCanceling, oneof: 0 15 | field :canceled, 5, type: LivebookProto.BillingStatusCanceled, oneof: 0 16 | end 17 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/billing_status_canceled.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.BillingStatusCanceled do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | end 4 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/billing_status_canceling.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.BillingStatusCanceling do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :cancel_at, 1, type: :int64, json_name: "cancelAt" 5 | end 6 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/billing_status_trial_ended.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.BillingStatusTrialEnded do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :trial_ends_at, 1, type: :int64, json_name: "trialEndsAt" 5 | end 6 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/billing_status_trialing.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.BillingStatusTrialing do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :trial_ends_at, 1, type: :int64, json_name: "trialEndsAt" 5 | end 6 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/deployment_group.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.DeploymentGroup do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | field :name, 2, type: :string 6 | field :mode, 3, type: :string 7 | field :secrets, 4, repeated: true, type: LivebookProto.DeploymentGroupSecret 8 | field :clustering, 5, type: :string 9 | field :zta_provider, 6, type: :string, json_name: "ztaProvider", deprecated: true 10 | field :zta_key, 7, type: :string, json_name: "ztaKey", deprecated: true 11 | field :agent_keys, 8, repeated: true, type: LivebookProto.AgentKey, json_name: "agentKeys" 12 | field :url, 9, type: :string 13 | 14 | field :environment_variables, 10, 15 | repeated: true, 16 | type: LivebookProto.EnvironmentVariable, 17 | json_name: "environmentVariables" 18 | 19 | field :teams_auth, 11, type: :bool, json_name: "teamsAuth" 20 | 21 | field :authorization_groups, 12, 22 | repeated: true, 23 | type: LivebookProto.AuthorizationGroup, 24 | json_name: "authorizationGroups" 25 | 26 | field :groups_auth, 13, type: :bool, json_name: "groupsAuth" 27 | end 28 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/deployment_group_created.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.DeploymentGroupCreated do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | field :name, 2, type: :string 6 | field :mode, 3, type: :string 7 | field :clustering, 5, type: :string 8 | field :zta_provider, 6, type: :string, json_name: "ztaProvider", deprecated: true 9 | field :zta_key, 7, type: :string, json_name: "ztaKey", deprecated: true 10 | field :agent_keys, 8, repeated: true, type: LivebookProto.AgentKey, json_name: "agentKeys" 11 | field :url, 9, type: :string 12 | field :teams_auth, 10, type: :bool, json_name: "teamsAuth" 13 | end 14 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/deployment_group_deleted.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.DeploymentGroupDeleted do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | end 6 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/deployment_group_secret.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.DeploymentGroupSecret do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :name, 1, type: :string 5 | field :value, 2, type: :string 6 | field :deployment_group_id, 3, type: :string, json_name: "deploymentGroupId" 7 | end 8 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/deployment_group_updated.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.DeploymentGroupUpdated do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | field :name, 2, type: :string 6 | field :secrets, 3, repeated: true, type: LivebookProto.DeploymentGroupSecret 7 | field :clustering, 4, type: :string 8 | field :zta_provider, 5, type: :string, json_name: "ztaProvider", deprecated: true 9 | field :zta_key, 6, type: :string, json_name: "ztaKey", deprecated: true 10 | field :agent_keys, 7, repeated: true, type: LivebookProto.AgentKey, json_name: "agentKeys" 11 | field :url, 8, type: :string 12 | 13 | field :environment_variables, 9, 14 | repeated: true, 15 | type: LivebookProto.EnvironmentVariable, 16 | json_name: "environmentVariables" 17 | 18 | field :teams_auth, 10, type: :bool, json_name: "teamsAuth" 19 | 20 | field :authorization_groups, 11, 21 | repeated: true, 22 | type: LivebookProto.AuthorizationGroup, 23 | json_name: "authorizationGroups" 24 | 25 | field :groups_auth, 12, type: :bool, json_name: "groupsAuth" 26 | end 27 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/environment_variable.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.EnvironmentVariable do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :name, 1, type: :string 5 | field :value, 2, type: :string 6 | end 7 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/error.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.Error do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :details, 1, type: :string 5 | end 6 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/file_system.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.FileSystem do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | field :name, 2, type: :string 6 | field :type, 3, type: :string 7 | field :value, 4, type: :string 8 | end 9 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/file_system_created.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.FileSystemCreated do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | field :name, 2, type: :string 6 | field :type, 3, type: :string 7 | field :value, 4, type: :string 8 | end 9 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/file_system_deleted.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.FileSystemDeleted do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | end 6 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/file_system_updated.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.FileSystemUpdated do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | field :name, 2, type: :string 6 | field :type, 3, type: :string 7 | field :value, 4, type: :string 8 | end 9 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/org_status_cancel.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.OrgStatusCancel do 2 | @moduledoc """ 3 | the org is or will be cancelled 4 | """ 5 | 6 | use Protobuf, protoc_gen_elixir_version: "0.13.0", syntax: :proto3 7 | 8 | field :cancel_at, 1, type: :int64, json_name: "cancelAt" 9 | end 10 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/org_status_trial.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.OrgStatusTrial do 2 | @moduledoc """ 3 | the org is on active or expired trial 4 | """ 5 | 6 | use Protobuf, protoc_gen_elixir_version: "0.13.0", syntax: :proto3 7 | 8 | field :trial_ends_at, 1, type: :int64, json_name: "trialEndsAt" 9 | end 10 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/org_updated.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.OrgUpdated do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | field :billing_status, 3, type: LivebookProto.BillingStatus, json_name: "billingStatus" 6 | end 7 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/secret.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.Secret do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :name, 1, type: :string 5 | field :value, 2, type: :string 6 | end 7 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/secret_created.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.SecretCreated do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :name, 1, type: :string 5 | field :value, 2, type: :string 6 | end 7 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/secret_deleted.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.SecretDeleted do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :name, 1, type: :string 5 | end 6 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/secret_updated.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.SecretUpdated do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :name, 1, type: :string 5 | field :value, 2, type: :string 6 | end 7 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/user_connected.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.UserConnected do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :name, 1, type: :string 5 | field :secrets, 2, repeated: true, type: LivebookProto.Secret 6 | field :file_systems, 3, repeated: true, type: LivebookProto.FileSystem, json_name: "fileSystems" 7 | 8 | field :deployment_groups, 4, 9 | repeated: true, 10 | type: LivebookProto.DeploymentGroup, 11 | json_name: "deploymentGroups" 12 | 13 | field :app_deployments, 5, 14 | repeated: true, 15 | type: LivebookProto.AppDeployment, 16 | json_name: "appDeployments" 17 | 18 | field :agents, 6, repeated: true, type: LivebookProto.Agent 19 | field :billing_status, 7, type: LivebookProto.BillingStatus, json_name: "billingStatus" 20 | end 21 | -------------------------------------------------------------------------------- /proto/lib/livebook_proto/user_deleted.pb.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.UserDeleted do 2 | use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 3 | 4 | field :id, 1, type: :string 5 | end 6 | -------------------------------------------------------------------------------- /proto/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule LivebookProto.MixProject do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :livebook_proto, 7 | version: "0.1.0", 8 | elixir: "~> 1.14", 9 | start_permanent: Mix.env() == :prod, 10 | deps: deps(), 11 | aliases: aliases() 12 | ] 13 | end 14 | 15 | def application do 16 | [extra_applications: [:logger]] 17 | end 18 | 19 | defp deps do 20 | [{:protobuf, "~> 0.12.0"}] 21 | end 22 | 23 | defp aliases do 24 | [ 25 | "protobuf.generate": [ 26 | "cmd protoc --elixir_out=one_file_per_module=true:lib --elixir_opt=include_docs=true --elixir_opt=gen_struct=true --elixir_opt=package_prefix=livebook_proto messages.proto", 27 | "format lib/livebook_proto/*.pb.ex", 28 | "deps.get", 29 | "compile" 30 | ] 31 | ] 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /proto/mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "protobuf": {:hex, :protobuf, "0.12.0", "58c0dfea5f929b96b5aa54ec02b7130688f09d2de5ddc521d696eec2a015b223", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "75fa6cbf262062073dd51be44dd0ab940500e18386a6c4e87d5819a58964dc45"}, 3 | } 4 | -------------------------------------------------------------------------------- /rel/app/env.bat.eex: -------------------------------------------------------------------------------- 1 | if exist "!USERPROFILE!\.livebookdesktop.bat" ( 2 | call "!USERPROFILE!\.livebookdesktop.bat" 3 | ) 4 | 5 | set RELEASE_MODE=interactive 6 | if not defined RELEASE_DISTRIBUTION set RELEASE_DISTRIBUTION=none 7 | 8 | set vendor_dir=!RELEASE_ROOT!\vendor\livebook-!RELEASE_VSN! 9 | set MIX_ARCHIVES=!vendor_dir!\archives 10 | set MIX_REBAR3=!vendor_dir!\rebar3 11 | if not defined LIVEBOOK_SHUTDOWN_ENABLED set LIVEBOOK_SHUTDOWN_ENABLED=true 12 | if not defined LIVEBOOK_PORT (set LIVEBOOK_PORT=32123) 13 | set PATH=!vendor_dir!\otp\erts-<%= @release.erts_version%>\bin;!vendor_dir!\otp\bin;!vendor_dir!\elixir\bin;!PATH! 14 | 15 | if defined LIVEBOOK_NODE set RELEASE_NODE=!LIVEBOOK_NODE! 16 | if defined LIVEBOOK_COOKIE set RELEASE_COOKIE=!LIVEBOOK_COOKIE! 17 | 18 | if not defined RELEASE_COOKIE ( 19 | REM Create random cookie. Removes spaces using : =%. Do not remove this comment. 20 | set RELEASE_COOKIE=cookie-%DATE: =%_%TIME: =%_%RANDOM% 21 | ) 22 | 23 | cd !HOMEDRIVE!!HOMEPATH! 24 | -------------------------------------------------------------------------------- /rel/app/env.sh.eex: -------------------------------------------------------------------------------- 1 | if [ -f "$HOME/.livebookdesktop.sh" ]; then 2 | . "$HOME/.livebookdesktop.sh" 3 | fi 4 | 5 | export RELEASE_MODE="interactive" 6 | if [ -z "${RELEASE_DISTRIBUTION}" ]; then export RELEASE_DISTRIBUTION="none"; fi 7 | 8 | vendor_dir="${RELEASE_ROOT}/vendor/livebook-${RELEASE_VSN}" 9 | export MIX_ARCHIVES="${vendor_dir}/archives" 10 | export MIX_REBAR3="${vendor_dir}/rebar3" 11 | export LIVEBOOK_SHUTDOWN_ENABLED=${LIVEBOOK_SHUTDOWN_ENABLED:-true} 12 | [ -z "$LIVEBOOK_PORT" ] && export LIVEBOOK_PORT=32123 13 | export PATH="${vendor_dir}/otp/erts-<%= @release.erts_version%>/bin:${vendor_dir}/otp/bin:${vendor_dir}/elixir/bin:$PATH" 14 | 15 | if [ ! -z "${LIVEBOOK_NODE}" ]; then export RELEASE_NODE=${LIVEBOOK_NODE}; fi 16 | if [ ! -z "${LIVEBOOK_COOKIE}" ]; then export RELEASE_COOKIE=${LIVEBOOK_COOKIE}; fi 17 | 18 | export RELEASE_COOKIE="${RELEASE_COOKIE:-$(cat /dev/urandom | env LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)}" 19 | 20 | cd $HOME 21 | -------------------------------------------------------------------------------- /rel/app/macos/.gitignore: -------------------------------------------------------------------------------- 1 | /.build 2 | /Packages 3 | /*.xcodeproj 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/config/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /rel/app/macos/App.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-jit 6 | 7 | com.apple.security.cs.allow-unsigned-executable-memory 8 | 9 | com.apple.security.cs.allow-dyld-environment-variables 10 | 11 | com.apple.security.cs.disable-library-validation 12 | 13 | com.apple.security.device.camera 14 | 15 | com.apple.security.device.audio-input 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-128px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-128px.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-16px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-16px.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-256px-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-256px-1.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-256px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-256px.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-32px-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-32px-1.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-32px.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-512px-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-512px-1.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-512px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-512px.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/AppIcon.appiconset/AppIcon-64px.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/Icon.iconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_128x128.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/Icon.iconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/Icon.iconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_16x16.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/Icon.iconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/Icon.iconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_256x256.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/Icon.iconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/Icon.iconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_32x32.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/Icon.iconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/Icon.iconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_512x512.png -------------------------------------------------------------------------------- /rel/app/macos/Assets.xcassets/Icon.iconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/macos/Assets.xcassets/Icon.iconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /rel/app/macos/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.5 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Livebook", 7 | platforms: [ 8 | .macOS(.v11) 9 | ], 10 | dependencies: [ 11 | .package(name: "ElixirKit", path: "../../../elixirkit/elixirkit_swift") 12 | ], 13 | targets: [ 14 | .executableTarget( 15 | name: "Livebook", 16 | dependencies: ["ElixirKit"] 17 | ) 18 | ] 19 | ) 20 | -------------------------------------------------------------------------------- /rel/app/macos/README.md: -------------------------------------------------------------------------------- 1 | # Livebook for macOS 2 | 3 | Run the app: 4 | 5 | $ ./run.sh 6 | -------------------------------------------------------------------------------- /rel/app/macos/build_app.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | export MIX_TARGET=app 5 | export MIX_ENV=prod 6 | export ELIXIRKIT_APP_NAME=Livebook 7 | export ELIXIRKIT_PROJECT_DIR=$PWD/../../.. 8 | export ELIXIRKIT_RELEASE_NAME=app 9 | 10 | . ../../../elixirkit/elixirkit_swift/Scripts/build_macos_app.sh 11 | -------------------------------------------------------------------------------- /rel/app/macos/build_dmg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | . `dirname $0`/build_app.sh 5 | . ../../../elixirkit/elixirkit_swift/Scripts/build_macos_dmg.sh 6 | -------------------------------------------------------------------------------- /rel/app/macos/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | . `dirname $0`/build_app.sh 5 | 6 | # Deregister /Applications/Livebook.app so that the "dev version" will handle .livemd and livebook:// 7 | /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \ 8 | -u /Applications/Livebook.app \ 9 | || true 10 | 11 | /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \ 12 | "$app_dir" 13 | 14 | open -W --stdout `tty` --stderr `tty` "$app_dir" 15 | -------------------------------------------------------------------------------- /rel/app/vm.args.eex: -------------------------------------------------------------------------------- 1 | # Disable busy waiting so that we don't waste resources 2 | # Limit the maximal number of ports for the same reason 3 | # Set the custom EPMD module 4 | +sbwt none +sbwtdcpu none +sbwtdio none +Q 65536 -epmd_module Elixir.Livebook.EPMD 5 | -------------------------------------------------------------------------------- /rel/app/windows/.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /obj 3 | -------------------------------------------------------------------------------- /rel/app/windows/App.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | PerMonitorV2 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /rel/app/windows/Livebook.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | WinExe 6 | bin/Livebook-$(Configuration) 7 | bin/Livebook-$(Configuration) 8 | net4.6-windows 9 | 10.0 10 | win-x64 11 | 12 | false 13 | true 14 | true 15 | Resources/AppIcon.ico 16 | App.manifest 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /rel/app/windows/README.md: -------------------------------------------------------------------------------- 1 | # Livebook for Windows 2 | 3 | Run the app: 4 | 5 | $ ./run.sh 6 | -------------------------------------------------------------------------------- /rel/app/windows/Resources/AppIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/app/windows/Resources/AppIcon.ico -------------------------------------------------------------------------------- /rel/app/windows/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | . `dirname $0`/env.sh 5 | 6 | rm -rf $target_dir 7 | dotnet build Livebook.csproj $build_args 8 | 9 | ( 10 | cd ../../.. 11 | mix release app --overwrite --path=${target_dir}/rel 12 | ) 13 | -------------------------------------------------------------------------------- /rel/app/windows/build_installer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | . `dirname $0`/env.sh 5 | 6 | rm -rf $target_dir 7 | dotnet publish Livebook.csproj $build_args 8 | 9 | ( 10 | cd ../../.. 11 | mix release app --overwrite --path=${target_dir}/rel 12 | ) 13 | 14 | vc_redist_path="bin/vc_redist.x64.exe" 15 | if [ ! -f $vc_redist_path ]; then 16 | url="https://aka.ms/vs/17/release/vc_redist.x64.exe" 17 | echo "downloading $url" 18 | curl -L --fail --output $vc_redist_path $url 19 | fi 20 | 21 | makensis \ 22 | //DERTS_VERSION=`elixir -e "IO.puts :erlang.system_info(:version)"` \ 23 | //DLIVEBOOK_VERSION=`elixir -e "Mix.start() ; Mix.Project.in_project(:livebook, \"../../..\", fn _ -> IO.puts Mix.Project.config()[:version] end)"` \ 24 | Installer.nsi 25 | -------------------------------------------------------------------------------- /rel/app/windows/env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | export MIX_ENV=prod 5 | export MIX_TARGET=app 6 | export ELIXIRKIT_APP_NAME=Livebook 7 | export ELIXIRKIT_PROJECT_DIR=$PWD/../../.. 8 | export ELIXIRKIT_RELEASE_NAME=app 9 | 10 | configuration=${ELIXIRKIT_CONFIGURATION:-Debug} 11 | target_dir="$PWD/bin/${ELIXIRKIT_APP_NAME}-$configuration" 12 | build_args="--configuration $configuration" 13 | -------------------------------------------------------------------------------- /rel/app/windows/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | . `dirname $0`/build.sh 5 | 6 | root="HKEY_CURRENT_USER\\Software\\Classes" 7 | 8 | exe="`cmd //c cd`\\bin\\Livebook-$configuration\\Livebook.exe" 9 | 10 | echo 11 | echo "!!!!!!" 12 | echo "Setting registry to use $exe" 13 | echo "!!!!!!" 14 | echo 15 | 16 | # .livemd 17 | reg add "$root\\.livemd" //d "Livebook.LiveMarkdown" //f 18 | reg add "$root\\Livebook.LiveMarkdown\\DefaultIcon" //d "$exe,1" //f 19 | reg add "$root\\Livebook.LiveMarkdown\\shell\\open\\command" //d "\"$exe\" \"open:%1\"" //f 20 | 21 | # livebook:// 22 | reg add "$root\\livebook" //d "URL:Livebook Protocol" //f 23 | reg add "$root\\livebook" //v "URL Protocol" //f 24 | reg add "$root\\livebook\\shell\\open\\command" //d "\"$exe\" \"open:%1\"" //f 25 | 26 | dotnet run --no-build 27 | -------------------------------------------------------------------------------- /rel/server/env.bat.eex: -------------------------------------------------------------------------------- 1 | if "!RELEASE_COMMAND!"=="rpc" goto remote 2 | if "!RELEASE_COMMAND!"=="remote" goto remote 3 | goto server 4 | 5 | :server 6 | if exist "!RELEASE_ROOT!\user\env.bat" ( 7 | call "!RELEASE_ROOT!\user\env.bat" 8 | ) 9 | 10 | set RELEASE_MODE=interactive 11 | set RELEASE_DISTRIBUTION=none 12 | 13 | if not defined RELEASE_COOKIE ( 14 | REM Create random cookie. Removes spaces using : =%. Do not remove this comment. 15 | set RELEASE_COOKIE=cookie-%DATE: =%_%TIME: =%_%RANDOM% 16 | ) 17 | 18 | cd !HOMEDRIVE!!HOMEPATH! 19 | goto end 20 | 21 | :remote 22 | set RELEASE_DISTRIBUTION=name 23 | if defined LIVEBOOK_NODE set RELEASE_NODE=!LIVEBOOK_NODE! 24 | if defined LIVEBOOK_COOKIE set RELEASE_COOKIE=!LIVEBOOK_COOKIE! 25 | goto end 26 | 27 | :end 28 | -------------------------------------------------------------------------------- /rel/server/overlays/bin/server: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | cd -P -- "$(dirname -- "$0")" 4 | 5 | # Livebook does not start EPMD automatically, but we want to start it 6 | # here, becasue we need it for clustering 7 | epmd -daemon 8 | 9 | if [ -n "${FLAME_PARENT}" ]; then 10 | exec elixir ./start_flame.exs 11 | elif [ -n "${LIVEBOOK_RUNTIME}" ]; then 12 | # Note: keep the flags in sync with the standalone runtime 13 | erl_flags="+sbwt none +sbwtdcpu none +sbwtdio none +sssdio 128 -elixir ansi_enabled true -noinput" 14 | 15 | # We add Livebook modules to the path, so that they are loaded from 16 | # from disk, rather than having module binaries sent from the parent 17 | # node. This cuts down the initialization time. 18 | livebook_beams="$(dirname -- "$(pwd)")/lib/livebook_runtime_ebin" 19 | erl_flags="$erl_flags -pa $livebook_beams" 20 | 21 | exec elixir --erl "$erl_flags" ./start_runtime.exs 22 | else 23 | exec ./livebook start 24 | fi 25 | -------------------------------------------------------------------------------- /rel/server/overlays/bin/start_flame.exs: -------------------------------------------------------------------------------- 1 | File.cd!(System.user_home!()) 2 | 3 | flame_parent = System.fetch_env!("FLAME_PARENT") |> Base.decode64!() |> :erlang.binary_to_term() 4 | 5 | %{ 6 | pid: parent_pid, 7 | flame_vsn: flame_parent_vsn, 8 | backend: _backend, 9 | backend_app: backend_app, 10 | backend_vsn: backend_vsn, 11 | node_base: node_base, 12 | host_env: host_env 13 | } = flame_parent 14 | 15 | flame_node_name = :"#{node_base}@#{System.fetch_env!(host_env)}" 16 | flame_node_cookie = String.to_atom(System.fetch_env!("LIVEBOOK_COOKIE")) 17 | 18 | flame_dep = 19 | if git_ref = System.get_env("FLAME_GIT_REF") do 20 | {:flame, github: "phoenixframework/flame", ref: git_ref} 21 | else 22 | {:flame, flame_parent_vsn} 23 | end 24 | 25 | flame_backend_deps = 26 | case backend_app do 27 | :flame -> [] 28 | _ -> [{backend_app, backend_vsn}] 29 | end 30 | 31 | {:ok, _} = :net_kernel.start(flame_node_name, %{name_domain: :longnames}) 32 | Node.set_cookie(flame_node_cookie) 33 | 34 | Mix.install([flame_dep | flame_backend_deps], consolidate_protocols: false) 35 | 36 | IO.puts( 37 | "[Livebook] starting #{inspect(flame_node_name)} in FLAME mode with parent: #{inspect(parent_pid)}, backend: #{inspect(backend_app)}" 38 | ) 39 | 40 | System.no_halt(true) 41 | -------------------------------------------------------------------------------- /rel/server/overlays/bin/warmup_apps: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd -P -- "$(dirname -- "$0")" 4 | exec ./livebook eval Livebook.Release.warmup_apps 5 | -------------------------------------------------------------------------------- /rel/server/overlays/bin/warmup_apps.bat: -------------------------------------------------------------------------------- 1 | call "%~dp0\livebook" eval Livebook.Release.warmup_apps 2 | -------------------------------------------------------------------------------- /rel/server/overlays/user/extensions/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/rel/server/overlays/user/extensions/.gitkeep -------------------------------------------------------------------------------- /rel/server/remote.vm.args.eex: -------------------------------------------------------------------------------- 1 | # Disable busy waiting so that we don't waste resources 2 | # Limit the maximal number of ports for the same reason 3 | +sbwt none +sbwtdcpu none +sbwtdio none +Q 65536 4 | 5 | # Set custom EPMD module and port 6 | -epmd_module Elixir.Livebook.EPMD -erl_epmd_port 13825 7 | 8 | # Disable listening and does not start epmd (due to RELEASE_DISTRIBUTION=name) 9 | -dist_listen false -start_epmd false 10 | -------------------------------------------------------------------------------- /rel/server/vm.args.eex: -------------------------------------------------------------------------------- 1 | # Disable busy waiting so that we don't waste resources 2 | # Limit the maximal number of ports for the same reason 3 | +sbwt none +sbwtdcpu none +sbwtdio none +Q 65536 4 | 5 | # Set custom EPMD module and port 6 | # 7 | # Note we don't use -erl_epmd_port here on purpose, 8 | # because that also assumes all nodes we connect to 9 | # run on erl_epmd_port and Livebook's attached node 10 | # needs to be able to connect regurlarly. 11 | -epmd_module Elixir.Livebook.EPMD -kernel inet_dist_listen_min 13825 12 | -------------------------------------------------------------------------------- /static/assets/KaTeX_AMS-Regular-CYEKBG2K.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_AMS-Regular-CYEKBG2K.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_AMS-Regular-JKX5W2C4.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_AMS-Regular-JKX5W2C4.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_AMS-Regular-U6PRYMIZ.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_AMS-Regular-U6PRYMIZ.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Caligraphic-Bold-5QL5CMTE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Caligraphic-Bold-5QL5CMTE.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Caligraphic-Bold-WZ3QSGD3.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Caligraphic-Bold-WZ3QSGD3.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Caligraphic-Bold-ZTS3R3HK.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Caligraphic-Bold-ZTS3R3HK.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_Caligraphic-Regular-3LKEU76G.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Caligraphic-Regular-3LKEU76G.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Caligraphic-Regular-A7XRTZ5Q.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Caligraphic-Regular-A7XRTZ5Q.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_Caligraphic-Regular-KX5MEWCF.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Caligraphic-Regular-KX5MEWCF.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Fraktur-Bold-2QVFK6NQ.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Fraktur-Bold-2QVFK6NQ.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Fraktur-Bold-T4SWXBMT.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Fraktur-Bold-T4SWXBMT.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Fraktur-Bold-WGHVTYOR.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Fraktur-Bold-WGHVTYOR.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_Fraktur-Regular-2PEIFJSJ.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Fraktur-Regular-2PEIFJSJ.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Fraktur-Regular-5U4OPH2X.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Fraktur-Regular-5U4OPH2X.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_Fraktur-Regular-PQMHCIK6.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Fraktur-Regular-PQMHCIK6.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Main-Bold-2GA4IZIN.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Bold-2GA4IZIN.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Main-Bold-W5FBVCZM.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Bold-W5FBVCZM.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_Main-Bold-YP5VVQRP.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Bold-YP5VVQRP.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Main-BoldItalic-4P4C7HJH.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-BoldItalic-4P4C7HJH.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Main-BoldItalic-N4V3DX7S.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-BoldItalic-N4V3DX7S.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Main-BoldItalic-ODMLBJJQ.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-BoldItalic-ODMLBJJQ.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_Main-Italic-I43T2HSR.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Italic-I43T2HSR.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_Main-Italic-RELBIK7M.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Italic-RELBIK7M.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Main-Italic-SASNQFN2.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Italic-SASNQFN2.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Main-Regular-ARRPAO67.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Regular-ARRPAO67.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Main-Regular-P5I74A2A.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Regular-P5I74A2A.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Main-Regular-W74P5G27.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Main-Regular-W74P5G27.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_Math-BoldItalic-6EBV3DK5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Math-BoldItalic-6EBV3DK5.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Math-BoldItalic-K4WTGH3J.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Math-BoldItalic-K4WTGH3J.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Math-BoldItalic-VB447A4D.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Math-BoldItalic-VB447A4D.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_Math-Italic-6KGCHLFN.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Math-Italic-6KGCHLFN.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Math-Italic-KKK3USB2.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Math-Italic-KKK3USB2.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Math-Italic-SON4MRCA.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Math-Italic-SON4MRCA.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_SansSerif-Bold-RRNVJFFW.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Bold-RRNVJFFW.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_SansSerif-Bold-STQ6RXC7.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Bold-STQ6RXC7.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_SansSerif-Bold-X5M5EMOD.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Bold-X5M5EMOD.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_SansSerif-Italic-HMPFTM52.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Italic-HMPFTM52.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_SansSerif-Italic-PSN4QKYX.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Italic-PSN4QKYX.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_SansSerif-Italic-WTBAZBGY.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Italic-WTBAZBGY.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_SansSerif-Regular-2TL3USAE.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Regular-2TL3USAE.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_SansSerif-Regular-OQCII6EP.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Regular-OQCII6EP.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_SansSerif-Regular-XIQ62X4E.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_SansSerif-Regular-XIQ62X4E.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Script-Regular-72OLXYNA.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Script-Regular-72OLXYNA.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_Script-Regular-A5IFOEBS.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Script-Regular-A5IFOEBS.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Script-Regular-APUWIHLP.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Script-Regular-APUWIHLP.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Size1-Regular-4HRHTS65.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size1-Regular-4HRHTS65.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Size1-Regular-5LRUTBFT.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size1-Regular-5LRUTBFT.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Size1-Regular-7K6AASVL.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size1-Regular-7K6AASVL.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_Size2-Regular-222HN3GT.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size2-Regular-222HN3GT.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_Size2-Regular-K5ZHAIS6.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size2-Regular-K5ZHAIS6.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Size2-Regular-LELKET5D.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size2-Regular-LELKET5D.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Size3-Regular-TLFPAHDE.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size3-Regular-TLFPAHDE.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Size3-Regular-UFCO6WCA.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size3-Regular-UFCO6WCA.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_Size3-Regular-WQRQ47UD.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size3-Regular-WQRQ47UD.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Size4-Regular-7PGNVPQK.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size4-Regular-7PGNVPQK.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_Size4-Regular-CDMV7U5C.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size4-Regular-CDMV7U5C.woff2 -------------------------------------------------------------------------------- /static/assets/KaTeX_Size4-Regular-PKMWZHNC.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Size4-Regular-PKMWZHNC.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Typewriter-Regular-3F5K6SQ6.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Typewriter-Regular-3F5K6SQ6.ttf -------------------------------------------------------------------------------- /static/assets/KaTeX_Typewriter-Regular-MJMFSK64.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Typewriter-Regular-MJMFSK64.woff -------------------------------------------------------------------------------- /static/assets/KaTeX_Typewriter-Regular-VBYJ4NRC.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/KaTeX_Typewriter-Regular-VBYJ4NRC.woff2 -------------------------------------------------------------------------------- /static/assets/architecture-I3QFYML2-6IR3LVRE.js: -------------------------------------------------------------------------------- 1 | import{a as e,b as r}from"./chunk-WNVR7S66.js";import"./chunk-SA2EGKJT.js";import"./chunk-WYMAA4MH.js";import"./chunk-SISR4MA5.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";export{e as ArchitectureModule,r as createArchitectureServices}; 2 | -------------------------------------------------------------------------------- /static/assets/chunk-5JLOMFCN.js: -------------------------------------------------------------------------------- 1 | import{b as u,c as y,d as f,e as h}from"./chunk-IDH2HD35.js";import{b as g,e as m,h as d}from"./chunk-FIA6NTGC.js";import{c as l}from"./chunk-47P5NBBB.js";import{D as n,N as s,h as o,j as a}from"./chunk-KRX7QNR4.js";var p={common:s,getConfig:n,insertCluster:m,insertEdge:f,insertEdgeLabel:u,insertMarkers:h,insertNode:d,interpolateToCurve:l,labelHelper:g,log:a,positionEdgeLabel:y},t={},L=o(r=>{for(let e of r)t[e.name]=e},"registerLayoutLoaders"),w=o(()=>{L([{name:"dagre",loader:o(async()=>await import("./dagre-4EVJKHTY-TI3ZPXZV.js"),"loader")}])},"registerDefaultLayoutLoaders");w();var R=o(async(r,e)=>{if(!(r.layoutAlgorithm in t))throw new Error(`Unknown layout algorithm: ${r.layoutAlgorithm}`);let i=t[r.layoutAlgorithm];return(await i.loader()).render(r,e,p,{algorithm:i.algorithm})},"render"),_=o((r="",{fallback:e="dagre"}={})=>{if(r in t)return r;if(e in t)return a.warn(`Layout algorithm ${r} is not registered. Using ${e} as fallback.`),e;throw new Error(`Both layout algorithms ${r} and ${e} are not registered.`)},"getRegisteredLayoutAlgorithm");export{L as a,R as b,_ as c}; 2 | -------------------------------------------------------------------------------- /static/assets/chunk-5XJO6U7A.js: -------------------------------------------------------------------------------- 1 | import{Z as m,h as c,ia as e}from"./chunk-KRX7QNR4.js";var v=c(t=>{var n,s;let{securityLevel:l}=m(),o=e("body");if(l==="sandbox"){let r=(s=(n=e(`#i${t}`).node())==null?void 0:n.contentDocument)!=null?s:document;o=e(r.body)}return o.select(`#${t}`)},"selectSvgElement");export{v as a}; 2 | -------------------------------------------------------------------------------- /static/assets/chunk-BQJTSJOB.js: -------------------------------------------------------------------------------- 1 | import{O as x,h as n,ia as a,j as h}from"./chunk-KRX7QNR4.js";var b=n((t,e)=>{let o;return e==="sandbox"&&(o=a("#i"+t)),(e==="sandbox"?a(o.nodes()[0].contentDocument.body):a("body")).select(`[id="${t}"]`)},"getDiagramElement"),B=n((t,e,o,i)=>{t.attr("class",o);let{width:r,height:s,x:m,y:d}=g(t,e);x(t,s,r,i);let c=w(m,d,r,s,e);t.attr("viewBox",c),h.debug(`viewBox configured: ${c} with padding: ${e}`)},"setupViewPortForSVG"),g=n((t,e)=>{var i;let o=((i=t.node())==null?void 0:i.getBBox())||{width:0,height:0,x:0,y:0};return{width:o.width+e*2,height:o.height+e*2,x:o.x,y:o.y}},"calculateDimensionsWithPadding"),w=n((t,e,o,i,r)=>`${t-r} ${e-r} ${o} ${i}`,"createViewBox");export{b as a,B as b}; 2 | -------------------------------------------------------------------------------- /static/assets/chunk-E4RBDSW5.js: -------------------------------------------------------------------------------- 1 | import{f as a}from"./chunk-SA2EGKJT.js";var i={},n={info:a(async()=>{let{createInfoServices:r}=await import("./info-46DW6VJ7-VCNA5FQQ.js"),e=r().Info.parser.LangiumParser;i.info=e},"info"),packet:a(async()=>{let{createPacketServices:r}=await import("./packet-W2GHVCYJ-FC4ERNLH.js"),e=r().Packet.parser.LangiumParser;i.packet=e},"packet"),pie:a(async()=>{let{createPieServices:r}=await import("./pie-BEWT4RHE-5KCD7UAH.js"),e=r().Pie.parser.LangiumParser;i.pie=e},"pie"),architecture:a(async()=>{let{createArchitectureServices:r}=await import("./architecture-I3QFYML2-6IR3LVRE.js"),e=r().Architecture.parser.LangiumParser;i.architecture=e},"architecture"),gitGraph:a(async()=>{let{createGitGraphServices:r}=await import("./gitGraph-YCYPL57B-J4JOQTHP.js"),e=r().GitGraph.parser.LangiumParser;i.gitGraph=e},"gitGraph")};async function p(r,e){let s=n[r];if(!s)throw new Error(`Unknown diagram type: ${r}`);i[r]||await s();let t=i[r].parse(e);if(t.lexerErrors.length>0||t.parserErrors.length>0)throw new m(t);return t.value}a(p,"parse");var c,m=(c=class extends Error{constructor(e){let s=e.lexerErrors.map(t=>t.message).join(` 2 | `),o=e.parserErrors.map(t=>t.message).join(` 3 | `);super(`Parsing failed: ${s} ${o}`),this.result=e}},a(c,"MermaidParseError"),c);export{p as a}; 4 | -------------------------------------------------------------------------------- /static/assets/chunk-HTMW6R2V.js: -------------------------------------------------------------------------------- 1 | import{h as i}from"./chunk-KRX7QNR4.js";var t,e=(t=class{constructor(r){this.init=r,this.records=this.init()}reset(){this.records=this.init()}},i(t,"ImperativeState"),t);export{e as a}; 2 | -------------------------------------------------------------------------------- /static/assets/chunk-MF5TUIBL.js: -------------------------------------------------------------------------------- 1 | import{h as l}from"./chunk-KRX7QNR4.js";function m(e,c){var i,t,o;e.accDescr&&((i=c.setAccDescription)==null||i.call(c,e.accDescr)),e.accTitle&&((t=c.setAccTitle)==null||t.call(c,e.accTitle)),e.title&&((o=c.setDiagramTitle)==null||o.call(c,e.title))}l(m,"populateCommonDb");export{m as a}; 2 | -------------------------------------------------------------------------------- /static/assets/chunk-NKCQAART.js: -------------------------------------------------------------------------------- 1 | import{a as n,b as s,c as o,d as u,e as C,f as e,g as d,j as c,m as l,o as m}from"./chunk-SA2EGKJT.js";var r,P=(r=class extends m{constructor(){super(["pie","showData"])}},e(r,"PieTokenBuilder"),r),t,p=(t=class extends l{runCustomConverter(i,a,f){if(i.name==="PIE_SECTION_LABEL")return a.replace(/"/g,"").trim()}},e(t,"PieValueConverter"),t),M={parser:{TokenBuilder:e(()=>new P,"TokenBuilder"),ValueConverter:e(()=>new p,"ValueConverter")}};function S(v=u){let i=o(s(v),d),a=o(n({shared:i}),c,M);return i.ServiceRegistry.register(a),{shared:i,Pie:a}}e(S,"createPieServices");export{M as a,S as b}; 2 | -------------------------------------------------------------------------------- /static/assets/chunk-SISR4MA5.js: -------------------------------------------------------------------------------- 1 | import{B as d,D as v,G as P,a as e,c as u,f as i,g as a,i as m,t as w,v as f,y as b,z as j}from"./chunk-24JW6VB3.js";var A=w(Object.keys,Object),S=A;var C=Object.prototype,V=C.hasOwnProperty;function D(r){if(!f(r))return S(r);var t=[];for(var o in Object(r))V.call(r,o)&&o!="constructor"&&t.push(o);return t}var T=D;var K=a(e,"DataView"),n=K;var N=a(e,"Promise"),s=N;var W=a(e,"Set"),c=W;var B=a(e,"WeakMap"),g=B;var O="[object Map]",z="[object Object]",M="[object Promise]",h="[object Set]",x="[object WeakMap]",k="[object DataView]",E=i(n),G=i(m),L=i(s),q=i(c),F=i(g),p=u;(n&&p(new n(new ArrayBuffer(1)))!=k||m&&p(new m)!=O||s&&p(s.resolve())!=M||c&&p(new c)!=h||g&&p(new g)!=x)&&(p=function(r){var t=u(r),o=t==z?r.constructor:void 0,y=o?i(o):"";if(y)switch(y){case E:return k;case G:return O;case L:return M;case q:return h;case F:return x}return t});var l=p;var H="[object Map]",I="[object Set]",J=Object.prototype,Q=J.hasOwnProperty;function R(r){if(r==null)return!0;if(d(r)&&(j(r)||typeof r=="string"||typeof r.splice=="function"||v(r)||P(r)||b(r)))return!r.length;var t=l(r);if(t==H||t==I)return!r.size;if(f(r))return!T(r).length;for(var o in r)if(Q.call(r,o))return!1;return!0}var kr=R;export{T as a,c as b,l as c,kr as d}; 2 | -------------------------------------------------------------------------------- /static/assets/chunk-TZMIYTTY.js: -------------------------------------------------------------------------------- 1 | import{a as c,b as n,c as a,d as i,e as m,f as r,g as u,i as d,n as l,o as s}from"./chunk-SA2EGKJT.js";var e,v=(e=class extends s{constructor(){super(["packet-beta"])}},r(e,"PacketTokenBuilder"),e),p={parser:{TokenBuilder:r(()=>new v,"TokenBuilder"),ValueConverter:r(()=>new l,"ValueConverter")}};function M(k=i){let t=a(n(k),u),o=a(c({shared:t}),d,p);return t.ServiceRegistry.register(o),{shared:t,Packet:o}}r(M,"createPacketServices");export{p as a,M as b}; 2 | -------------------------------------------------------------------------------- /static/assets/chunk-UY4K4Z6W.js: -------------------------------------------------------------------------------- 1 | import{a,b as i,c as n,d as u,e as m,f as r,g as d,h as s,n as l,o as c}from"./chunk-SA2EGKJT.js";var e,v=(e=class extends c{constructor(){super(["info","showInfo"])}},r(e,"InfoTokenBuilder"),e),I={parser:{TokenBuilder:r(()=>new v,"TokenBuilder"),ValueConverter:r(()=>new l,"ValueConverter")}};function M(f=u){let o=n(i(f),d),t=n(a({shared:o}),s,I);return o.ServiceRegistry.register(t),{shared:o,Info:t}}r(M,"createInfoServices");export{I as a,M as b}; 2 | -------------------------------------------------------------------------------- /static/assets/chunk-VW6A6SAT.js: -------------------------------------------------------------------------------- 1 | import{h as g}from"./chunk-KRX7QNR4.js";var b=g(({flowchart:i})=>{var t,p,r,s;let e=(p=(t=i==null?void 0:i.subGraphTitleMargin)==null?void 0:t.top)!=null?p:0,n=(s=(r=i==null?void 0:i.subGraphTitleMargin)==null?void 0:r.bottom)!=null?s:0,T=e+n;return{subGraphTitleTopMargin:e,subGraphTitleBottomMargin:n,subGraphTitleTotalMargin:T}},"getSubGraphTitleMargins");export{b as a}; 2 | -------------------------------------------------------------------------------- /static/assets/chunk-W7LTUAGZ.js: -------------------------------------------------------------------------------- 1 | var r="11.4.1";export{r as a}; 2 | -------------------------------------------------------------------------------- /static/assets/chunk-WNVR7S66.js: -------------------------------------------------------------------------------- 1 | import{a as u,b as n,c as i,d as o,e as A,f as t,g as s,k as l,m as d,o as m}from"./chunk-SA2EGKJT.js";var c,C=(c=class extends m{constructor(){super(["architecture"])}},t(c,"ArchitectureTokenBuilder"),c),a,v=(a=class extends d{runCustomConverter(e,r,M){if(e.name==="ARCH_ICON")return r.replace(/[()]/g,"").trim();if(e.name==="ARCH_TEXT_ICON")return r.replace(/["()]/g,"");if(e.name==="ARCH_TITLE")return r.replace(/[[\]]/g,"").trim()}},t(a,"ArchitectureValueConverter"),a),T={parser:{TokenBuilder:t(()=>new C,"TokenBuilder"),ValueConverter:t(()=>new v,"ValueConverter")}};function f(h=o){let e=i(n(h),s),r=i(u({shared:e}),l,T);return e.ServiceRegistry.register(r),{shared:e,Architecture:r}}t(f,"createArchitectureServices");export{T as a,f as b}; 2 | -------------------------------------------------------------------------------- /static/assets/chunk-ZP5QXAVO.js: -------------------------------------------------------------------------------- 1 | import{a as o,b as n,c as a,d as u,e as p,f as r,g as d,l,n as s,o as G}from"./chunk-SA2EGKJT.js";var e,h=(e=class extends G{constructor(){super(["gitGraph"])}},r(e,"GitGraphTokenBuilder"),e),m={parser:{TokenBuilder:r(()=>new h,"TokenBuilder"),ValueConverter:r(()=>new s,"ValueConverter")}};function v(c=u){let t=a(n(c),d),i=a(o({shared:t}),l,m);return t.ServiceRegistry.register(i),{shared:t,GitGraph:i}}r(v,"createGitGraphServices");export{m as a,v as b}; 2 | -------------------------------------------------------------------------------- /static/assets/classDiagram-LNE6IOMH-QE5QBIDP.js: -------------------------------------------------------------------------------- 1 | import{a as e,b as a,c as i,d as s}from"./chunk-IJWB56EA.js";import"./chunk-BQJTSJOB.js";import"./chunk-5JLOMFCN.js";import"./chunk-IDH2HD35.js";import"./chunk-FIA6NTGC.js";import"./chunk-JVEBZTDG.js";import"./chunk-VW6A6SAT.js";import"./chunk-NCYNIMJ4.js";import"./chunk-ZPWCXWQI.js";import"./chunk-47P5NBBB.js";import"./chunk-XHGORZV2.js";import{h as t}from"./chunk-KRX7QNR4.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";var g={parser:e,db:a,renderer:s,styles:i,init:t(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute,a.clear()},"init")};export{g as diagram}; 2 | -------------------------------------------------------------------------------- /static/assets/classDiagram-v2-MQ7JQ4JX-6A4BS74I.js: -------------------------------------------------------------------------------- 1 | import{a as e,b as a,c as i,d as s}from"./chunk-IJWB56EA.js";import"./chunk-BQJTSJOB.js";import"./chunk-5JLOMFCN.js";import"./chunk-IDH2HD35.js";import"./chunk-FIA6NTGC.js";import"./chunk-JVEBZTDG.js";import"./chunk-VW6A6SAT.js";import"./chunk-NCYNIMJ4.js";import"./chunk-ZPWCXWQI.js";import"./chunk-47P5NBBB.js";import"./chunk-XHGORZV2.js";import{h as t}from"./chunk-KRX7QNR4.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";var g={parser:e,db:a,renderer:s,styles:i,init:t(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute,a.clear()},"init")};export{g as diagram}; 2 | -------------------------------------------------------------------------------- /static/assets/gitGraph-YCYPL57B-J4JOQTHP.js: -------------------------------------------------------------------------------- 1 | import{a as r,b as e}from"./chunk-ZP5QXAVO.js";import"./chunk-SA2EGKJT.js";import"./chunk-WYMAA4MH.js";import"./chunk-SISR4MA5.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";export{r as GitGraphModule,e as createGitGraphServices}; 2 | -------------------------------------------------------------------------------- /static/assets/info-46DW6VJ7-VCNA5FQQ.js: -------------------------------------------------------------------------------- 1 | import{a as o,b as e}from"./chunk-UY4K4Z6W.js";import"./chunk-SA2EGKJT.js";import"./chunk-WYMAA4MH.js";import"./chunk-SISR4MA5.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";export{o as InfoModule,e as createInfoServices}; 2 | -------------------------------------------------------------------------------- /static/assets/infoDiagram-A4XQUW5V-2UH4OEMN.js: -------------------------------------------------------------------------------- 1 | import{a as s}from"./chunk-W7LTUAGZ.js";import{a as i}from"./chunk-5XJO6U7A.js";import{a as g}from"./chunk-E4RBDSW5.js";import"./chunk-NKCQAART.js";import"./chunk-WNVR7S66.js";import"./chunk-ZP5QXAVO.js";import{O as n,h as r,j as a}from"./chunk-KRX7QNR4.js";import"./chunk-UY4K4Z6W.js";import"./chunk-TZMIYTTY.js";import"./chunk-SA2EGKJT.js";import"./chunk-WYMAA4MH.js";import"./chunk-SISR4MA5.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";var v={parse:r(async e=>{let t=await g("info",e);a.debug(t)},"parse")},d={version:s},m=r(()=>d.version,"getVersion"),c={getVersion:m},f=r((e,t,p)=>{a.debug(`rendering info diagram 2 | `+e);let o=i(t);n(o,100,400,!0),o.append("g").append("text").attr("x",100).attr("y",40).attr("class","version").attr("font-size",32).style("text-anchor","middle").text(`v${p}`)},"draw"),l={draw:f},y={parser:v,db:c,renderer:l};export{y as diagram}; 3 | -------------------------------------------------------------------------------- /static/assets/inter-cyrillic-400-normal-JTDOFDEL.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-400-normal-JTDOFDEL.woff -------------------------------------------------------------------------------- /static/assets/inter-cyrillic-400-normal-KR3WP37A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-400-normal-KR3WP37A.woff2 -------------------------------------------------------------------------------- /static/assets/inter-cyrillic-500-normal-L3ERAO2A.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-500-normal-L3ERAO2A.woff2 -------------------------------------------------------------------------------- /static/assets/inter-cyrillic-500-normal-YGY63224.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-500-normal-YGY63224.woff -------------------------------------------------------------------------------- /static/assets/inter-cyrillic-600-normal-RRU6DOYW.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-600-normal-RRU6DOYW.woff2 -------------------------------------------------------------------------------- /static/assets/inter-cyrillic-600-normal-YAIFIQFF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-600-normal-YAIFIQFF.woff -------------------------------------------------------------------------------- /static/assets/inter-cyrillic-ext-400-normal-ERANL6CF.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-ext-400-normal-ERANL6CF.woff2 -------------------------------------------------------------------------------- /static/assets/inter-cyrillic-ext-400-normal-NYJ3VVCY.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-ext-400-normal-NYJ3VVCY.woff -------------------------------------------------------------------------------- /static/assets/inter-cyrillic-ext-500-normal-3D2TL7BV.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-ext-500-normal-3D2TL7BV.woff2 -------------------------------------------------------------------------------- /static/assets/inter-cyrillic-ext-500-normal-MVWVUDWY.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-ext-500-normal-MVWVUDWY.woff -------------------------------------------------------------------------------- /static/assets/inter-cyrillic-ext-600-normal-3WK3AIAE.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-ext-600-normal-3WK3AIAE.woff2 -------------------------------------------------------------------------------- /static/assets/inter-cyrillic-ext-600-normal-ML7GDJ53.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-cyrillic-ext-600-normal-ML7GDJ53.woff -------------------------------------------------------------------------------- /static/assets/inter-greek-400-normal-22W3W6EP.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-400-normal-22W3W6EP.woff -------------------------------------------------------------------------------- /static/assets/inter-greek-400-normal-K452ZUNI.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-400-normal-K452ZUNI.woff2 -------------------------------------------------------------------------------- /static/assets/inter-greek-500-normal-SR6UVMZM.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-500-normal-SR6UVMZM.woff2 -------------------------------------------------------------------------------- /static/assets/inter-greek-500-normal-V5MWXBXZ.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-500-normal-V5MWXBXZ.woff -------------------------------------------------------------------------------- /static/assets/inter-greek-600-normal-52AI77RO.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-600-normal-52AI77RO.woff2 -------------------------------------------------------------------------------- /static/assets/inter-greek-600-normal-MHDVNT3B.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-600-normal-MHDVNT3B.woff -------------------------------------------------------------------------------- /static/assets/inter-greek-ext-400-normal-6GLOS2AC.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-ext-400-normal-6GLOS2AC.woff -------------------------------------------------------------------------------- /static/assets/inter-greek-ext-400-normal-7CMSSAC3.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-ext-400-normal-7CMSSAC3.woff2 -------------------------------------------------------------------------------- /static/assets/inter-greek-ext-500-normal-GTVYGHNV.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-ext-500-normal-GTVYGHNV.woff -------------------------------------------------------------------------------- /static/assets/inter-greek-ext-500-normal-L7SAHJMO.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-ext-500-normal-L7SAHJMO.woff2 -------------------------------------------------------------------------------- /static/assets/inter-greek-ext-600-normal-2BPZID6V.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-ext-600-normal-2BPZID6V.woff2 -------------------------------------------------------------------------------- /static/assets/inter-greek-ext-600-normal-URVVZOUP.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-greek-ext-600-normal-URVVZOUP.woff -------------------------------------------------------------------------------- /static/assets/inter-latin-400-normal-GYFJJKJF.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-400-normal-GYFJJKJF.woff2 -------------------------------------------------------------------------------- /static/assets/inter-latin-400-normal-HCV22YHT.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-400-normal-HCV22YHT.woff -------------------------------------------------------------------------------- /static/assets/inter-latin-500-normal-5GHDSDAY.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-500-normal-5GHDSDAY.woff2 -------------------------------------------------------------------------------- /static/assets/inter-latin-500-normal-PGDKKSLY.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-500-normal-PGDKKSLY.woff -------------------------------------------------------------------------------- /static/assets/inter-latin-600-normal-56WZKGNJ.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-600-normal-56WZKGNJ.woff2 -------------------------------------------------------------------------------- /static/assets/inter-latin-600-normal-RLL3LKKR.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-600-normal-RLL3LKKR.woff -------------------------------------------------------------------------------- /static/assets/inter-latin-ext-400-normal-2H3KLIXK.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-ext-400-normal-2H3KLIXK.woff -------------------------------------------------------------------------------- /static/assets/inter-latin-ext-400-normal-OMTSHYQS.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-ext-400-normal-OMTSHYQS.woff2 -------------------------------------------------------------------------------- /static/assets/inter-latin-ext-500-normal-R6CV3WNH.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-ext-500-normal-R6CV3WNH.woff2 -------------------------------------------------------------------------------- /static/assets/inter-latin-ext-500-normal-WO7CCPI4.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-ext-500-normal-WO7CCPI4.woff -------------------------------------------------------------------------------- /static/assets/inter-latin-ext-600-normal-IPOBZ3TF.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-ext-600-normal-IPOBZ3TF.woff2 -------------------------------------------------------------------------------- /static/assets/inter-latin-ext-600-normal-Q5LOCDKP.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-latin-ext-600-normal-Q5LOCDKP.woff -------------------------------------------------------------------------------- /static/assets/inter-vietnamese-400-normal-3ZH4IT4J.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-vietnamese-400-normal-3ZH4IT4J.woff2 -------------------------------------------------------------------------------- /static/assets/inter-vietnamese-400-normal-CQIZECWR.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-vietnamese-400-normal-CQIZECWR.woff -------------------------------------------------------------------------------- /static/assets/inter-vietnamese-500-normal-PQFGSX3P.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-vietnamese-500-normal-PQFGSX3P.woff2 -------------------------------------------------------------------------------- /static/assets/inter-vietnamese-500-normal-YWCA5IC5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-vietnamese-500-normal-YWCA5IC5.woff -------------------------------------------------------------------------------- /static/assets/inter-vietnamese-600-normal-RMFHJUBF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-vietnamese-600-normal-RMFHJUBF.woff -------------------------------------------------------------------------------- /static/assets/inter-vietnamese-600-normal-TVIYLGI7.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/inter-vietnamese-600-normal-TVIYLGI7.woff2 -------------------------------------------------------------------------------- /static/assets/jetbrains-mono-cyrillic-400-normal-C5JECUCT.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-cyrillic-400-normal-C5JECUCT.woff2 -------------------------------------------------------------------------------- /static/assets/jetbrains-mono-cyrillic-400-normal-ZHDMAAUB.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-cyrillic-400-normal-ZHDMAAUB.woff -------------------------------------------------------------------------------- /static/assets/jetbrains-mono-cyrillic-ext-400-normal-C5DDHB7N.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-cyrillic-ext-400-normal-C5DDHB7N.woff -------------------------------------------------------------------------------- /static/assets/jetbrains-mono-cyrillic-ext-400-normal-C7IFWGF6.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-cyrillic-ext-400-normal-C7IFWGF6.woff2 -------------------------------------------------------------------------------- /static/assets/jetbrains-mono-greek-400-normal-KF6EKC2J.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-greek-400-normal-KF6EKC2J.woff -------------------------------------------------------------------------------- /static/assets/jetbrains-mono-greek-400-normal-O7JTTR3P.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-greek-400-normal-O7JTTR3P.woff2 -------------------------------------------------------------------------------- /static/assets/jetbrains-mono-latin-400-normal-3OOWLGQ2.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-latin-400-normal-3OOWLGQ2.woff2 -------------------------------------------------------------------------------- /static/assets/jetbrains-mono-latin-400-normal-4JODGGIE.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-latin-400-normal-4JODGGIE.woff -------------------------------------------------------------------------------- /static/assets/jetbrains-mono-latin-ext-400-normal-3RACNWYK.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-latin-ext-400-normal-3RACNWYK.woff -------------------------------------------------------------------------------- /static/assets/jetbrains-mono-latin-ext-400-normal-JVR3IR4Z.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-latin-ext-400-normal-JVR3IR4Z.woff2 -------------------------------------------------------------------------------- /static/assets/jetbrains-mono-vietnamese-400-normal-7A56GUVN.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-vietnamese-400-normal-7A56GUVN.woff -------------------------------------------------------------------------------- /static/assets/jetbrains-mono-vietnamese-400-normal-KU7YLUPA.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/jetbrains-mono-vietnamese-400-normal-KU7YLUPA.woff2 -------------------------------------------------------------------------------- /static/assets/katex-R4V72ZOM.js: -------------------------------------------------------------------------------- 1 | import{a}from"./chunk-GJ6CDUB5.js";import"./chunk-MGYUK2XN.js";export{a as default}; 2 | -------------------------------------------------------------------------------- /static/assets/packet-W2GHVCYJ-FC4ERNLH.js: -------------------------------------------------------------------------------- 1 | import{a as e,b as r}from"./chunk-TZMIYTTY.js";import"./chunk-SA2EGKJT.js";import"./chunk-WYMAA4MH.js";import"./chunk-SISR4MA5.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";export{e as PacketModule,r as createPacketServices}; 2 | -------------------------------------------------------------------------------- /static/assets/pie-BEWT4RHE-5KCD7UAH.js: -------------------------------------------------------------------------------- 1 | import{a as e,b as r}from"./chunk-NKCQAART.js";import"./chunk-SA2EGKJT.js";import"./chunk-WYMAA4MH.js";import"./chunk-SISR4MA5.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";export{e as PieModule,r as createPieServices}; 2 | -------------------------------------------------------------------------------- /static/assets/red-hat-text-latin-400-normal-OV4KODUR.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/red-hat-text-latin-400-normal-OV4KODUR.woff2 -------------------------------------------------------------------------------- /static/assets/red-hat-text-latin-400-normal-YWAZCVDO.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/red-hat-text-latin-400-normal-YWAZCVDO.woff -------------------------------------------------------------------------------- /static/assets/red-hat-text-latin-ext-400-normal-H2UN6E6K.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/red-hat-text-latin-ext-400-normal-H2UN6E6K.woff -------------------------------------------------------------------------------- /static/assets/red-hat-text-latin-ext-400-normal-PD2NZNLC.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/red-hat-text-latin-ext-400-normal-PD2NZNLC.woff2 -------------------------------------------------------------------------------- /static/assets/remixicon-6QPK2BSM.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/remixicon-6QPK2BSM.ttf -------------------------------------------------------------------------------- /static/assets/remixicon-7463F4HZ.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/remixicon-7463F4HZ.woff2 -------------------------------------------------------------------------------- /static/assets/remixicon-DH65ADK5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/remixicon-DH65ADK5.woff -------------------------------------------------------------------------------- /static/assets/remixicon-OJBB7CW7.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/assets/remixicon-OJBB7CW7.eot -------------------------------------------------------------------------------- /static/assets/stateDiagram-v2-4JROLMXI-VMRUNBYI.js: -------------------------------------------------------------------------------- 1 | import{a,b as i,c as r,d as o}from"./chunk-6KI6TUY3.js";import"./chunk-BQJTSJOB.js";import"./chunk-5JLOMFCN.js";import"./chunk-IDH2HD35.js";import"./chunk-FIA6NTGC.js";import"./chunk-JVEBZTDG.js";import"./chunk-VW6A6SAT.js";import"./chunk-NCYNIMJ4.js";import"./chunk-ZPWCXWQI.js";import"./chunk-47P5NBBB.js";import"./chunk-XHGORZV2.js";import{h as e}from"./chunk-KRX7QNR4.js";import"./chunk-24JW6VB3.js";import"./chunk-MGYUK2XN.js";var k={parser:a,db:r,renderer:i,styles:o,init:e(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute,r.clear()},"init")};export{k as diagram}; 2 | -------------------------------------------------------------------------------- /static/favicons/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/favicons/favicon.png -------------------------------------------------------------------------------- /static/images/elixir-portal.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/elixir-portal.jpeg -------------------------------------------------------------------------------- /static/images/elixir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/elixir.png -------------------------------------------------------------------------------- /static/images/explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/explorer.png -------------------------------------------------------------------------------- /static/images/github-stars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/github-stars.png -------------------------------------------------------------------------------- /static/images/kino.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/kino.png -------------------------------------------------------------------------------- /static/images/logo-with-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/logo-with-text.png -------------------------------------------------------------------------------- /static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/logo.png -------------------------------------------------------------------------------- /static/images/maplibre.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/maplibre.png -------------------------------------------------------------------------------- /static/images/teams.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/teams.png -------------------------------------------------------------------------------- /static/images/vega_lite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/static/images/vega_lite.png -------------------------------------------------------------------------------- /static/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | ## Running Livebook test suite 2 | 3 | ### Unit tests 4 | 5 | ``` 6 | $ mix test 7 | ``` 8 | 9 | ### Teams tests 10 | 11 | Integration with Livebook Teams requires cloning the private Livebook Teams repository. By default it is expected to be in sibling directory to Livebook itself. But you can set `TEAMS_PATH` to a custom directory. 12 | 13 | Livebook should take care of automatically setting up the Teams repository for you. However, if you run into issues, you can run the following in the Teams repository: 14 | 15 | ``` 16 | $ MIX_ENV=livebook mix setup 17 | ``` 18 | -------------------------------------------------------------------------------- /test/livebook/ecto_types/hex_color_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Livebook.EctoTypes.HexColorTest do 2 | use ExUnit.Case, async: true 3 | doctest Livebook.EctoTypes.HexColor 4 | end 5 | -------------------------------------------------------------------------------- /test/livebook/epmd_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Livebook.EPMDTest do 2 | use ExUnit.Case, async: true 3 | 4 | test "has a custom dist port" do 5 | assert Livebook.EPMD.dist_port() != 0 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/livebook/hubs/personal_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Hubs.PersonalTest do 2 | use Livebook.DataCase 3 | 4 | alias Livebook.Hubs.Personal 5 | 6 | test "get_secrets/1 returns a list of secrets from storage" do 7 | secret = build(:secret) 8 | 9 | Personal.set_secret(secret) 10 | assert secret in Personal.get_secrets() 11 | 12 | Personal.unset_secret(secret.name) 13 | refute secret in Personal.get_secrets() 14 | end 15 | 16 | test "fetch an specific secret" do 17 | secret = insert_secret() 18 | 19 | assert_raise Livebook.Storage.NotFoundError, 20 | ~s(could not find entry in "hub_secrets" with ID "NOT_HERE"), 21 | fn -> 22 | Personal.fetch_secret!("NOT_HERE") 23 | end 24 | 25 | assert Personal.fetch_secret!(secret.name) == secret 26 | Personal.unset_secret(secret.name) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/livebook/learn_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Livebook.LearnTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias Livebook.Notebook 5 | alias Livebook.Notebook.Learn 6 | 7 | describe "notebook_by_slug!/1" do 8 | test "returns notebook structure and images if found" do 9 | assert {%Notebook{}, _imaegs} = Learn.notebook_by_slug!("intro-to-livebook") 10 | end 11 | 12 | test "raises an error if no matching notebook if found" do 13 | assert_raise Learn.NotFoundError, fn -> 14 | Learn.notebook_by_slug!("invalid-slug") 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/livebook/runtime/attached_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Runtime.AttachedTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias Livebook.Runtime 5 | 6 | describe "Runtime.connect/1" do 7 | test "given an invalid node returns an error" do 8 | runtime = Runtime.Attached.new(:nonexistent@node) 9 | pid = Runtime.connect(runtime) 10 | 11 | assert_receive {:runtime_connect_done, ^pid, 12 | {:error, "node :nonexistent@node is unreachable"}} 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /test/livebook/runtime/erl_dist/io_forward_gl_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Runtime.ErlDist.IOForwardGLTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias Livebook.Runtime.ErlDist.IOForwardGL 5 | 6 | test "forwards requests to sender's group leader" do 7 | pid = start_supervised!(IOForwardGL) 8 | 9 | group_leader_io = 10 | ExUnit.CaptureIO.capture_io(:stdio, fn -> 11 | # This sends an IO request to the IOForwardGL process. 12 | # Our group leader is :stdio (by default) so we expect 13 | # it to receive the string. 14 | IO.puts(pid, "hey") 15 | end) 16 | 17 | assert group_leader_io == "hey\n" 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/livebook/runtime/erl_dist/node_manager_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Runtime.ErlDist.NodeManagerTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias Livebook.Runtime 5 | alias Livebook.Runtime.ErlDist.NodeManager 6 | alias Livebook.Runtime.ErlDist.RuntimeServer 7 | 8 | test "terminates when the last runtime server terminates" do 9 | # We use a standalone runtime, so that we have an isolated node 10 | # with its own node manager 11 | pid = Runtime.Standalone.new() |> Runtime.connect() 12 | assert_receive {:runtime_connect_done, ^pid, {:ok, runtime}} 13 | %{node: node, server_pid: server1} = runtime 14 | 15 | Runtime.take_ownership(runtime) 16 | 17 | manager_pid = :erpc.call(node, Process, :whereis, [Livebook.Runtime.ErlDist.NodeManager]) 18 | ref = Process.monitor(manager_pid) 19 | 20 | {:ok, server2} = NodeManager.start_runtime_server(node) 21 | 22 | RuntimeServer.stop(server1) 23 | RuntimeServer.stop(server2) 24 | 25 | assert_receive {:DOWN, ^ref, :process, _, _} 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/livebook/runtime/erl_dist/smart_cell_gl_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Runtime.ErlDist.SmartCellGLTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias Livebook.Runtime.ErlDist.SmartCellGL 5 | 6 | test "forwards regular IO requests to its group leader" do 7 | assert ExUnit.CaptureIO.capture_io(:stdio, fn -> 8 | runtime_broadcast_to = self() 9 | pid = start_supervised!({SmartCellGL, runtime_broadcast_to}) 10 | 11 | IO.puts(pid, "hey") 12 | end) == "hey\n" 13 | end 14 | 15 | test "supports :livebook_get_broadcast_target request" do 16 | runtime_broadcast_to = self() 17 | pid = start_supervised!({SmartCellGL, runtime_broadcast_to}) 18 | 19 | assert {:ok, ^runtime_broadcast_to} = io_request(pid, :livebook_get_broadcast_target) 20 | end 21 | 22 | defp io_request(io, request) do 23 | ref = make_ref() 24 | send(io, {:io_request, self(), ref, request}) 25 | assert_receive {:io_reply, ^ref, reply} 26 | reply 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/livebook/secrets_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Livebook.SecretsTest do 2 | use Livebook.DataCase, async: true 3 | 4 | alias Livebook.Secrets 5 | alias Livebook.Secrets.Secret 6 | 7 | describe "update_secret/2" do 8 | test "returns a valid secret" do 9 | attrs = params_for(:secret) 10 | 11 | assert {:ok, secret} = Secrets.update_secret(%Secret{}, attrs) 12 | assert attrs.name == secret.name 13 | assert attrs.value == secret.value 14 | assert attrs.hub_id == secret.hub_id 15 | end 16 | 17 | test "returns changeset error" do 18 | attrs = params_for(:secret, name: nil, value: "111") 19 | assert {:error, changeset} = Secrets.update_secret(%Secret{}, attrs) 20 | assert "can't be blank" in errors_on(changeset).name 21 | 22 | attrs = params_for(:secret, name: "@inavalid", value: "111") 23 | assert {:error, changeset} = Secrets.update_secret(%Secret{}, attrs) 24 | 25 | assert "should contain only alphanumeric characters and underscore" in errors_on(changeset).name 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/livebook/users/user_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Users.UserTest do 2 | use Livebook.DataCase, async: true 3 | 4 | alias Livebook.Users.User 5 | 6 | describe "change/2" do 7 | test "given valid attributes returns and updated user" do 8 | user = build(:user) 9 | 10 | attrs = %{ 11 | "name" => "Jake Peralta", 12 | "hex_color" => "#000000", 13 | "payload" => %{"country" => "US"} 14 | } 15 | 16 | changeset = User.changeset(user, attrs) 17 | 18 | assert changeset.valid? 19 | assert get_field(changeset, :name) == "Jake Peralta" 20 | assert get_field(changeset, :hex_color) == "#000000" 21 | assert get_field(changeset, :payload) == %{"country" => "US"} 22 | end 23 | 24 | test "given invalid color returns an error" do 25 | user = build(:user) 26 | attrs = %{"hex_color" => "#invalid"} 27 | changeset = User.changeset(user, attrs) 28 | 29 | refute changeset.valid? 30 | assert "not a valid color" in errors_on(changeset).hex_color 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /test/livebook/users_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Livebook.UsersTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias Livebook.Users 5 | alias Livebook.Users.User 6 | 7 | describe "broadcast_change/1" do 8 | test "notifies subscribers of user change" do 9 | user = User.new() 10 | Users.subscribe(user.id) 11 | 12 | Users.broadcast_change(user) 13 | 14 | assert_received {:user_change, ^user} 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/livebook/utils/time_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Utils.TimeTest do 2 | use ExUnit.Case, async: true 3 | 4 | alias Livebook.Utils 5 | 6 | doctest Utils.Time 7 | end 8 | -------------------------------------------------------------------------------- /test/livebook_test.exs: -------------------------------------------------------------------------------- 1 | defmodule LivebookTest do 2 | use ExUnit.Case, async: true 3 | 4 | test "live_markdown_to_elixir/1" do 5 | markdown = """ 6 | # Lists 7 | 8 | ## Introduction 9 | 10 | Let's generate a list of numbers: 11 | 12 | ```elixir 13 | Enum.to_list(1..10) 14 | ``` 15 | """ 16 | 17 | assert Livebook.live_markdown_to_elixir(markdown) == """ 18 | # Run as: iex --dot-iex path/to/notebook.exs 19 | 20 | # Title: Lists 21 | 22 | # ── Introduction ── 23 | 24 | # Let's generate a list of numbers: 25 | 26 | Enum.to_list(1..10) 27 | """ 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/livebook_web/controllers/endpoint_test.exs: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.EndpointTest do 2 | use LivebookWeb.ConnCase, async: true 3 | 4 | test "delete cookies once they go over a certain limit", %{conn: conn} do 5 | cookies = 6 | Enum.map(1..5, &"c#{&1}=#{String.duplicate("a", 4096)}") ++ 7 | Enum.map(1..5, &"lb_#{&1}=#{String.duplicate("a", 4096)}") 8 | 9 | assert [ 10 | "c1=;" <> _, 11 | "c2=;" <> _, 12 | "c3=;" <> _, 13 | "c4=;" <> _, 14 | "c5=;" <> _, 15 | "lb_session" <> _, 16 | "lb_user_data" <> _ 17 | ] = 18 | conn 19 | |> put_req_header("cookie", Enum.join(cookies, "; ")) 20 | |> get(~p"/") 21 | |> get_resp_header("set-cookie") 22 | |> Enum.sort() 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/livebook_web/controllers/errors_test.exs: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.ErrorsTest do 2 | use LivebookWeb.ConnCase, async: true 3 | 4 | test "renders 404", %{conn: conn} do 5 | conn = get(conn, "/this/does/not/exist") 6 | 7 | assert conn.status == 404 8 | assert conn.resp_body =~ "No Numbats here!" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/livebook_web/controllers/user_controller_test.exs: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.UserControllerTest do 2 | use LivebookWeb.ConnCase, async: true 3 | 4 | describe "GET /logout" do 5 | test "redirects when already logged out", %{conn: conn} do 6 | assert conn 7 | |> Phoenix.ConnTest.init_test_session(%{}) 8 | |> get(~p"/logout") 9 | |> redirected_to() == ~p"/" 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/livebook_web/live/learn_live_test.exs: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.LearnLiveTest do 2 | use LivebookWeb.ConnCase, async: true 3 | 4 | import Phoenix.LiveViewTest 5 | 6 | test "link to introductory notebook correctly creates a new session", %{conn: conn} do 7 | {:ok, view, _} = live(conn, ~p"/learn") 8 | 9 | assert {:error, {:live_redirect, %{to: to}}} = 10 | view 11 | |> element(~s{#welcome-to-livebook a}, "Open notebook") 12 | |> render_click() 13 | 14 | assert to =~ "/sessions/" 15 | 16 | # Note that this LV page is huge and the simulated rendering in 17 | # LV tests is not heavily optimized. This LV receives events from 18 | # concurrent tests (such as hub creation) and rendering is a big 19 | # bottleneck, to the point where calling render(view) times out. 20 | # That's why we only assert on the dead render HTML. 21 | {:ok, _view, html} = live(conn, to) 22 | assert html =~ "Welcome to Livebook" 23 | 24 | close_session_by_path(to) 25 | end 26 | 27 | defp close_session_by_path("/sessions/" <> session_id) do 28 | {:ok, session} = Livebook.Sessions.fetch_session(session_id) 29 | Livebook.Session.close(session.pid) 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /test/livebook_web/live/output/plain_text_component_test.exs: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.Output.PlainTextComponentTest do 2 | use LivebookWeb.ConnCase, async: true 3 | import Phoenix.LiveViewTest 4 | 5 | alias LivebookWeb.Output.PlainTextComponent 6 | 7 | test "renders chunks with styles" do 8 | assigns = %{ 9 | id: "test-id", 10 | output: %{ 11 | text: "Hello", 12 | style: [color: "red", unknown: "discarded", font_size: "invalid;discarded"] 13 | } 14 | } 15 | 16 | content = render_component(PlainTextComponent, assigns) 17 | assert content =~ ~s|
Hello| 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/support/app_helpers.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.AppHelpers do 2 | def deploy_notebook_sync(notebook) do 3 | app_spec = Livebook.Apps.NotebookAppSpec.new(notebook) 4 | 5 | deployer_pid = Livebook.Apps.Deployer.local_deployer() 6 | ref = Livebook.Apps.Deployer.deploy_monitor(deployer_pid, app_spec) 7 | 8 | receive do 9 | {:deploy_result, ^ref, {:ok, pid}} -> 10 | Process.demonitor(ref, [:flush]) 11 | 12 | ExUnit.Callbacks.on_exit(fn -> 13 | if Process.alive?(pid) do 14 | Livebook.App.close(pid) 15 | end 16 | end) 17 | 18 | pid 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /test/support/assets.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/test/support/assets.tar.gz -------------------------------------------------------------------------------- /test/support/channel_case.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.ChannelCase do 2 | use ExUnit.CaseTemplate 3 | 4 | using do 5 | quote do 6 | # Import conveniences for testing with channels 7 | import Phoenix.ChannelTest 8 | 9 | # The default endpoint for testing 10 | @endpoint LivebookWeb.Endpoint 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/support/conn_case.ex: -------------------------------------------------------------------------------- 1 | defmodule LivebookWeb.ConnCase do 2 | use ExUnit.CaseTemplate 3 | 4 | using do 5 | quote do 6 | # Import conveniences for testing with connections 7 | import Plug.Conn 8 | import Phoenix.ConnTest 9 | import Livebook.Factory 10 | import LivebookWeb.ConnCase 11 | 12 | use LivebookWeb, :verified_routes 13 | 14 | # The default endpoint for testing 15 | @endpoint LivebookWeb.Endpoint 16 | end 17 | end 18 | 19 | setup tags do 20 | conn = Phoenix.ConnTest.build_conn() 21 | 22 | conn = 23 | if authentication = tags[:authentication] do 24 | with_authentication(conn, authentication) 25 | else 26 | conn 27 | end 28 | 29 | [conn: conn] 30 | end 31 | 32 | def with_authentication(conn, authentication) do 33 | Plug.Test.init_test_session(conn, %{authentication_test_override: authentication}) 34 | end 35 | 36 | def with_authorization(conn, id) do 37 | Plug.Test.init_test_session(conn, %{ 38 | identity_provider_test_override: {:zta, Livebook.ZTA.LivebookTeams, id} 39 | }) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /test/support/notebook_app_spec.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.Apps.NotebookAppSpec do 2 | # App spec carrying an in-memory notebook. 3 | 4 | defstruct [:slug, :notebook, :load_failures, :should_warmup, version: "1"] 5 | 6 | def new(notebook, opts \\ []) do 7 | opts = Keyword.validate!(opts, load_failures: 0, should_warmup: false) 8 | 9 | slug = notebook.app_settings.slug 10 | 11 | :persistent_term.put({slug, :failures}, 0) 12 | 13 | %__MODULE__{ 14 | slug: slug, 15 | notebook: notebook, 16 | load_failures: opts[:load_failures], 17 | should_warmup: opts[:should_warmup] 18 | } 19 | end 20 | end 21 | 22 | defimpl Livebook.Apps.AppSpec, for: Livebook.Apps.NotebookAppSpec do 23 | def load(app_spec, _files_tmp_path) do 24 | key = {app_spec.slug, :failures} 25 | num_failures = :persistent_term.get(key) 26 | 27 | if num_failures < app_spec.load_failures do 28 | :persistent_term.put(key, num_failures + 1) 29 | {:error, "failed to load"} 30 | else 31 | {:ok, %{notebook: app_spec.notebook, warnings: []}} 32 | end 33 | end 34 | 35 | def should_warmup?(app_spec) do 36 | app_spec.should_warmup 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /test/support/notebooks/basic.livemd: -------------------------------------------------------------------------------- 1 | # Notebook 2 | 3 | ## First section 4 | 5 | One Code cell below. 6 | 7 | ```elixir 8 | length([1, 2, 3]) 9 | ``` 10 | -------------------------------------------------------------------------------- /test/support/notebooks/with_two_sections.livemd: -------------------------------------------------------------------------------- 1 | # Notebook 2 | 3 | ## First section 4 | 5 | This is the first section. 6 | 7 | ```elixir 8 | length([1, 2, 3]) 9 | ``` 10 | 11 | ## Second section 12 | 13 | This is the second section. 14 | -------------------------------------------------------------------------------- /test/support/static/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/livebook-dev/livebook/e2d87fd5696ebff130e29a19843f0e7556d43f7f/test/support/static/icon.ico -------------------------------------------------------------------------------- /test/support/static/js/app.js: -------------------------------------------------------------------------------- 1 | console.log("Hello"); 2 | -------------------------------------------------------------------------------- /test/support/teams_integration_case.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.TeamsIntegrationCase do 2 | use ExUnit.CaseTemplate 3 | 4 | alias Livebook.TeamsServer 5 | 6 | using do 7 | quote do 8 | use Livebook.DataCase 9 | use LivebookWeb.ConnCase 10 | 11 | @moduletag :teams_integration 12 | 13 | alias Livebook.TeamsServer 14 | alias Livebook.TeamsRPC 15 | 16 | import Livebook.HubHelpers 17 | import Livebook.TeamsIntegrationHelper 18 | end 19 | end 20 | 21 | setup_all do 22 | case TeamsServer.start() do 23 | {:ok, _} -> :ok 24 | {:error, {:already_started, _}} -> :ok 25 | end 26 | 27 | url = TeamsServer.url() 28 | node = TeamsServer.get_node() 29 | 30 | Application.put_env(:livebook, :teams_url, url, persistent: true) 31 | 32 | {:ok, node: node} 33 | end 34 | 35 | setup context do 36 | if topics = context[:subscribe_to_hubs_topics] do 37 | Livebook.Hubs.Broadcasts.subscribe(topics) 38 | end 39 | 40 | if topics = context[:subscribe_to_teams_topics] do 41 | Livebook.Teams.Broadcasts.subscribe(topics) 42 | end 43 | 44 | :ok 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /test/support/test_modules/bad_inspect.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.TestModules.BadInspect do 2 | defstruct [] 3 | 4 | defimpl Inspect do 5 | @dialyzer {:nowarn_function, inspect: 2} 6 | 7 | def inspect(%Livebook.TestModules.BadInspect{}, _opts) do 8 | :bad_return 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/support/test_modules/hidden.ex: -------------------------------------------------------------------------------- 1 | defmodule Livebook.TestModules.Hidden do 2 | @moduledoc false 3 | def visible, do: :ok 4 | 5 | @doc false 6 | def hidden, do: :ok 7 | end 8 | -------------------------------------------------------------------------------- /versions: -------------------------------------------------------------------------------- 1 | elixir="1.18.3" 2 | otp="27.3.3" 3 | openssl="1.1.1s" 4 | rebar3="3.22.0" 5 | ubuntu="noble-20250404" 6 | --------------------------------------------------------------------------------