From 902c993098c8ff124a7486bd38f35505d532582c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Mon, 4 Apr 2022 12:19:11 +0200 Subject: [PATCH] Adjustments (#1087) * Fix horizontal scrollbar on smaller screens * Apply navigation shortcuts without an additional roundtrip * Shorten the data element selector * Fix URL in changelog * Return reference from handle_intellisense --- CHANGELOG.md | 2 +- assets/css/js_interop.css | 202 +++++++++--------- assets/css/markdown.css | 8 +- assets/js/hooks/cell.js | 11 +- assets/js/hooks/cell_editor.js | 2 +- assets/js/hooks/headline.js | 2 +- assets/js/hooks/js_view.js | 10 +- assets/js/hooks/session.js | 54 +++-- lib/livebook/runtime.ex | 4 +- lib/livebook/runtime/attached.ex | 4 +- lib/livebook/runtime/elixir_standalone.ex | 4 +- lib/livebook/runtime/embedded.ex | 4 +- .../runtime/erl_dist/runtime_server.ex | 7 +- lib/livebook/runtime/mix_standalone.ex | 4 +- lib/livebook_web/live/home_live.ex | 2 +- .../live/home_live/session_list_component.ex | 8 +- lib/livebook_web/live/output.ex | 2 +- .../live/output/input_component.ex | 12 +- .../live/output/stdout_component.ex | 2 +- .../live/output/text_component.ex | 2 +- lib/livebook_web/live/session_live.ex | 59 +++-- .../live/session_live/cell_component.ex | 46 ++-- .../session_live/cell_editor_component.ex | 2 +- .../live/session_live/indicators_component.ex | 10 +- .../session_live/insert_buttons_component.ex | 2 +- .../live/session_live/section_component.ex | 8 +- lib/livebook_web/live/sidebar_helpers.ex | 8 +- .../runtime/erl_dist/runtime_server_test.exs | 20 +- test/livebook_web/live/home_live_test.exs | 2 +- test/livebook_web/live/session_live_test.exs | 26 +-- test/support/noop_runtime.ex | 2 +- 31 files changed, 256 insertions(+), 275 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3cd9e256..2999caf65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -## [v0.5.2](https://github.com/livebook-dev/livebook/tree/v0.5.1) (2022-01-27) +## [v0.5.2](https://github.com/livebook-dev/livebook/tree/v0.5.2) (2022-01-27) ### Added diff --git a/assets/css/js_interop.css b/assets/css/js_interop.css index aabee8d59..0e317c2ed 100644 --- a/assets/css/js_interop.css +++ b/assets/css/js_interop.css @@ -19,93 +19,91 @@ solely client-side operations. /* === Session === */ -[data-element="session"]:not([data-js-insert-mode]) - [data-element="insert-mode-indicator"] { +[data-el-session]:not([data-js-insert-mode]) [data-el-insert-mode-indicator] { @apply invisible; } -[data-element="session"]:not([data-js-insert-mode]) - [data-element="cell"][data-type="markdown"] - [data-element="editor-box"], -[data-element="session"][data-js-insert-mode] - [data-element="cell"][data-type="markdown"]:not([data-js-focused]) - [data-element="editor-box"] { +[data-el-session]:not([data-js-insert-mode]) + [data-el-cell][data-type="markdown"] + [data-el-editor-box], +[data-el-session][data-js-insert-mode] + [data-el-cell][data-type="markdown"]:not([data-js-focused]) + [data-el-editor-box] { @apply hidden; } -[data-element="session"][data-js-insert-mode] - [data-element="cell"][data-js-focused] - [data-element="enable-insert-mode-button"] { +[data-el-session][data-js-insert-mode] + [data-el-cell][data-js-focused] + [data-el-enable-insert-mode-button] { @apply hidden; } -[data-element="session"]:not([data-js-insert-mode]) - [data-element="cell"][data-type="markdown"][data-js-focused] - [data-element="insert-image-button"] { +[data-el-session]:not([data-js-insert-mode]) + [data-el-cell][data-type="markdown"][data-js-focused] + [data-el-insert-image-button] { @apply hidden; } -[data-element="notebook-headline"]:hover [data-element="heading"], -[data-element="section-headline"]:hover [data-element="heading"] { +[data-el-notebook-headline]:hover [data-el-heading], +[data-el-section-headline]:hover [data-el-heading] { @apply border-blue-200; } -[data-element="notebook-headline"][data-js-focused] [data-element="heading"], -[data-element="section-headline"][data-js-focused] [data-element="heading"] { +[data-el-notebook-headline][data-js-focused] [data-el-heading], +[data-el-section-headline][data-js-focused] [data-el-heading] { @apply border-blue-300; } -[data-element="section-headline"]:not(:hover):not([data-js-focused]) - [data-element="heading"] - + [data-element="section-actions"]:not(:focus-within) { +[data-el-section-headline]:not(:hover):not([data-js-focused]) + [data-el-heading] + + [data-el-section-actions]:not(:focus-within) { @apply hidden; } -[data-element="cell"][data-js-focused] { +[data-el-cell][data-js-focused] { @apply border-blue-300 border-opacity-100; } -[data-element="cell"]:not([data-js-focused]) - [data-element="actions"]:not(:focus-within) { +[data-el-cell]:not([data-js-focused]) [data-el-actions]:not(:focus-within) { /* Note: using opacity, so the buttons are focusable (via tab navigation) and when focused we show the actions back. */ @apply opacity-0; } -[data-element="cell"]:not([data-js-focused]) - [data-element="actions"]:not([data-primary]):not(:focus-within) { +[data-el-cell]:not([data-js-focused]) + [data-el-actions]:not([data-primary]):not(:focus-within) { @apply pointer-events-none; } -[data-element="cell"]:not([data-js-focused])[data-js-hover] - [data-element="actions"][data-primary] { +[data-el-cell]:not([data-js-focused])[data-js-hover] + [data-el-actions][data-primary] { @apply opacity-100 pointer-events-auto; } -[data-element="cell"][data-js-changed] [data-element="cell-status"] { +[data-el-cell][data-js-changed] [data-el-cell-status] { @apply italic; } -[data-element="cell"]:not([data-js-changed]) - [data-element="cell-status"] - [data-element="change-indicator"] { +[data-el-cell]:not([data-js-changed]) + [data-el-cell-status] + [data-el-change-indicator] { @apply invisible; } -[data-element="sections-list-item"][data-js-is-viewed] { +[data-el-sections-list-item][data-js-is-viewed] { @apply text-gray-900; } -[data-element="cell"]:not([data-js-focused])[data-js-hover] - [data-element="cell-focus-indicator"] { +[data-el-cell]:not([data-js-focused])[data-js-hover] + [data-el-cell-focus-indicator] { @apply bg-blue-200; } -[data-element="cell"][data-js-focused] [data-element="cell-focus-indicator"] { +[data-el-cell][data-js-focused] [data-el-cell-focus-indicator] { @apply bg-blue-300; } -[data-element="cell"][data-js-amplified] [data-element="outputs-container"] { +[data-el-cell][data-js-amplified] [data-el-outputs-container] { @apply bg-white m-0 py-16; width: 90vw; @@ -113,155 +111,151 @@ solely client-side operations. left: calc(-45vw + 50%); } -[data-element="cell"][data-js-amplified] - [data-element="amplify-outputs-button"] - [data-element="zoom-in-icon"] { +[data-el-cell][data-js-amplified] + [data-el-amplify-outputs-button] + [data-el-zoom-in-icon] { @apply hidden; } -[data-element="cell"]:not([data-js-amplified]) - [data-element="amplify-outputs-button"] - [data-element="zoom-out-icon"] { +[data-el-cell]:not([data-js-amplified]) + [data-el-amplify-outputs-button] + [data-el-zoom-out-icon] { @apply hidden; } -[data-element="cell"][data-type="smart"]:not([data-js-source-visible]) - [data-element="editor-box"] { +[data-el-cell][data-type="smart"]:not([data-js-source-visible]) + [data-el-editor-box] { /* Note: we intentionally don't hide the editor, because this leads to issues with hover intellisense once the editor is shown. */ @apply h-0 overflow-hidden; } -[data-element="cell"][data-type="smart"][data-js-source-visible] - [data-element="ui-box"] { +[data-el-cell][data-type="smart"][data-js-source-visible] [data-el-ui-box] { @apply hidden; } -[data-element="session"]:not([data-js-insert-mode]) - [data-element="cell"][data-type="setup"]:not([data-eval-validity="fresh"]:not([data-js-empty])):not([data-js-changed]) - [data-element="editor-box"], -[data-element="session"] - [data-element="cell"][data-type="setup"]:not([data-eval-validity="fresh"]:not([data-js-empty])):not([data-js-changed]):not([data-js-focused]) - [data-element="editor-box"] { +[data-el-session]:not([data-js-insert-mode]) + [data-el-cell][data-type="setup"]:not([data-eval-validity="fresh"]:not([data-js-empty])):not([data-js-changed]) + [data-el-editor-box], +[data-el-session] + [data-el-cell][data-type="setup"]:not([data-eval-validity="fresh"]:not([data-js-empty])):not([data-js-changed]):not([data-js-focused]) + [data-el-editor-box] { @apply h-0 overflow-hidden; } -[data-element="session"][data-js-insert-mode] - [data-element="cell"][data-type="setup"][data-js-focused] - [data-element="enable-insert-mode-button"], -[data-element="session"] - [data-element="cell"][data-type="setup"][data-eval-validity="fresh"]:not([data-js-empty]) - [data-element="enable-insert-mode-button"], -[data-element="session"] - [data-element="cell"][data-type="setup"][data-js-changed] - [data-element="enable-insert-mode-button"] { +[data-el-session][data-js-insert-mode] + [data-el-cell][data-type="setup"][data-js-focused] + [data-el-enable-insert-mode-button], +[data-el-session] + [data-el-cell][data-type="setup"][data-eval-validity="fresh"]:not([data-js-empty]) + [data-el-enable-insert-mode-button], +[data-el-session] + [data-el-cell][data-type="setup"][data-js-changed] + [data-el-enable-insert-mode-button] { @apply hidden; } -[data-element="session"][data-js-insert-mode] - [data-element="cell"][data-type="setup"][data-js-focused] - [data-element="info-box"], -[data-element="session"] - [data-element="cell"][data-type="setup"][data-eval-validity="fresh"]:not([data-js-empty]) - [data-element="info-box"], -[data-element="session"] - [data-element="cell"][data-type="setup"][data-js-changed] - [data-element="info-box"] { +[data-el-session][data-js-insert-mode] + [data-el-cell][data-type="setup"][data-js-focused] + [data-el-info-box], +[data-el-session] + [data-el-cell][data-type="setup"][data-eval-validity="fresh"]:not([data-js-empty]) + [data-el-info-box], +[data-el-session] + [data-el-cell][data-type="setup"][data-js-changed] + [data-el-info-box] { @apply hidden; } -[data-element="cell"][data-type="smart"]:not([data-js-source-visible]) - [data-element="show-ui-icon"] { +[data-el-cell][data-type="smart"]:not([data-js-source-visible]) + [data-el-show-ui-icon] { @apply hidden; } -[data-element="cell"][data-type="smart"][data-js-source-visible] - [data-element="show-code-icon"] { +[data-el-cell][data-type="smart"][data-js-source-visible] + [data-el-show-code-icon] { @apply hidden; } -[data-element="cell"][data-type="smart"][data-js-source-visible] - [data-element="cell-status-container"] { +[data-el-cell][data-type="smart"][data-js-source-visible] + [data-el-cell-status-container] { @apply absolute bottom-2 right-2; } -[data-element="output"]:not([data-wrapper]) { +[data-el-output]:not([data-wrapper]) { @apply py-4; } -[data-element="output"][data-border] { +[data-el-output][data-border] { @apply px-4 border border-t-0 border-gray-200 divide-y divide-gray-200; } -[data-element="output"][data-border]:first-child { +[data-el-output][data-border]:first-child { @apply rounded-t-lg border-t; } -[data-element="output"]:not([data-border]) - + [data-element="output"][data-border] { +[data-el-output]:not([data-border]) + [data-el-output][data-border] { @apply border-t; } -[data-element="output"][data-border]:last-child { +[data-el-output][data-border]:last-child { @apply rounded-b-lg border-b; } -[data-element="output"]:not(:first-child) { +[data-el-output]:not(:first-child) { @apply mt-2; } -[data-element="output"][data-border] + [data-element="output"][data-border] { +[data-el-output][data-border] + [data-el-output][data-border] { @apply mt-0; } -[data-element="outputs-container"] > [data-element="output"]:first-child { +[data-el-outputs-container] > [data-el-output]:first-child { @apply mt-2; } -[data-element="session"]:not([data-js-side-panel-content]) - [data-element="side-panel"] { +[data-el-session]:not([data-js-side-panel-content]) [data-el-side-panel] { @apply hidden; } -[data-element="session"]:not([data-js-side-panel-content="sections-list"]) - [data-element="sections-list"] { +[data-el-session]:not([data-js-side-panel-content="sections-list"]) + [data-el-sections-list] { @apply hidden; } -[data-element="session"]:not([data-js-side-panel-content="clients-list"]) - [data-element="clients-list"] { +[data-el-session]:not([data-js-side-panel-content="clients-list"]) + [data-el-clients-list] { @apply hidden; } -[data-element="session"]:not([data-js-side-panel-content="runtime-info"]) - [data-element="runtime-info"] { +[data-el-session]:not([data-js-side-panel-content="runtime-info"]) + [data-el-runtime-info] { @apply hidden; } -[data-element="session"][data-js-side-panel-content="sections-list"] - [data-element="sections-list-toggle"] { +[data-el-session][data-js-side-panel-content="sections-list"] + [data-el-sections-list-toggle] { @apply text-gray-50 bg-gray-700; } -[data-element="session"][data-js-side-panel-content="clients-list"] - [data-element="clients-list-toggle"] { +[data-el-session][data-js-side-panel-content="clients-list"] + [data-el-clients-list-toggle] { @apply text-gray-50 bg-gray-700; } -[data-element="session"][data-js-side-panel-content="runtime-info"] - [data-element="runtime-info-toggle"] { +[data-el-session][data-js-side-panel-content="runtime-info"] + [data-el-runtime-info-toggle] { @apply text-gray-50 bg-gray-700; } -[data-element="clients-list-item"]:not([data-js-followed]) - [data-meta="unfollow"] { +[data-el-clients-list-item]:not([data-js-followed]) [data-meta="unfollow"] { @apply hidden; } -[data-element="clients-list-item"][data-js-followed] [data-meta="follow"] { +[data-el-clients-list-item][data-js-followed] [data-meta="follow"] { @apply hidden; } -[phx-hook="VirtualizedLines"]:not(:hover) [data-element="clipcopy"] { +[phx-hook="VirtualizedLines"]:not(:hover) [data-el-clipcopy] { @apply hidden; } diff --git a/assets/css/markdown.css b/assets/css/markdown.css index 4a1847baa..34217f86a 100644 --- a/assets/css/markdown.css +++ b/assets/css/markdown.css @@ -145,13 +145,13 @@ /* Overrides for user-entered markdown */ -[data-element="cell"][data-type="markdown"] .markdown h1, -[data-element="cell"][data-type="markdown"] .markdown h2 { +[data-el-cell][data-type="markdown"] .markdown h1, +[data-el-cell][data-type="markdown"] .markdown h2 { font-size: 0; } -[data-element="cell"][data-type="markdown"] .markdown h1:after, -[data-element="cell"][data-type="markdown"] .markdown h2:after { +[data-el-cell][data-type="markdown"] .markdown h1:after, +[data-el-cell][data-type="markdown"] .markdown h2:after { @apply text-red-400 text-base font-medium; content: "warning: heading levels 1 and 2 are reserved for notebook and section names, please use heading 3 and above."; } diff --git a/assets/js/hooks/cell.js b/assets/js/hooks/cell.js index 6ce0b5593..ae409b339 100644 --- a/assets/js/hooks/cell.js +++ b/assets/js/hooks/cell.js @@ -35,7 +35,7 @@ const Cell = { if (this.props.type === "code") { const amplifyButton = this.el.querySelector( - `[data-element="amplify-outputs-button"]` + `[data-el-amplify-outputs-button]` ); amplifyButton.addEventListener("click", (event) => { this.el.toggleAttribute("data-js-amplified"); @@ -44,7 +44,7 @@ const Cell = { if (this.props.type === "smart") { const toggleSourceButton = this.el.querySelector( - `[data-element="toggle-source-button"]` + `[data-el-toggle-source-button]` ); toggleSourceButton.addEventListener("click", (event) => { this.el.toggleAttribute("data-js-source-visible"); @@ -185,7 +185,7 @@ const Cell = { // Setup markdown rendering if (this.props.type === "markdown") { const markdownContainer = this.el.querySelector( - `[data-element="markdown-container"]` + `[data-el-markdown-container]` ); const markdown = new Markdown(markdownContainer, source, { baseUrl: this.props.sessionPath, @@ -254,10 +254,9 @@ const Cell = { }, updateChangeIndicator() { - const cellStatus = this.el.querySelector(`[data-element="cell-status"]`); + const cellStatus = this.el.querySelector(`[data-el-cell-status]`); const indicator = - cellStatus && - cellStatus.querySelector(`[data-element="change-indicator"]`); + cellStatus && cellStatus.querySelector(`[data-el-change-indicator]`); if (indicator && this.props.evaluationDigest) { const source = this.liveEditors.primary.getSource(); diff --git a/assets/js/hooks/cell_editor.js b/assets/js/hooks/cell_editor.js index 265eaadd8..d6885fcc2 100644 --- a/assets/js/hooks/cell_editor.js +++ b/assets/js/hooks/cell_editor.js @@ -9,7 +9,7 @@ const CellEditor = { `cell_editor_init:${this.props.cellId}:${this.props.tag}`, ({ source_view, language, intellisense, read_only }) => { const editorContainer = this.el.querySelector( - `[data-element="editor-container"]` + `[data-el-editor-container]` ); // Remove the content placeholder diff --git a/assets/js/hooks/headline.js b/assets/js/hooks/headline.js index d519b7fca..cc0acae24 100644 --- a/assets/js/hooks/headline.js +++ b/assets/js/hooks/headline.js @@ -52,7 +52,7 @@ const Headline = { }, initializeHeadingEl() { - const headingEl = this.el.querySelector(`[data-element="heading"]`); + const headingEl = this.el.querySelector(`[data-el-heading]`); if (headingEl === this.headingEl) { return; diff --git a/assets/js/hooks/js_view.js b/assets/js/hooks/js_view.js index b1df0c3fd..e5bc4e2ff 100644 --- a/assets/js/hooks/js_view.js +++ b/assets/js/hooks/js_view.js @@ -165,9 +165,9 @@ const JSView = { this.iframe = document.createElement("iframe"); this.iframe.className = "w-full h-0 absolute z-[1]"; - const notebookEl = document.querySelector(`[data-element="notebook"]`); + const notebookEl = document.querySelector(`[data-el-notebook]`); const notebookContentEl = notebookEl.querySelector( - `[data-element="notebook-content"]` + `[data-el-notebook-content]` ); // Most placeholder position changes are accompanied by changes to the @@ -219,7 +219,7 @@ const JSView = { repositionIframe() { const { iframe, iframePlaceholder } = this; - const notebookEl = document.querySelector(`[data-element="notebook"]`); + const notebookEl = document.querySelector(`[data-el-notebook]`); if (iframePlaceholder.offsetParent === null) { // When the placeholder is hidden, we hide the iframe as well @@ -239,9 +239,7 @@ const JSView = { }, loadIframe() { - const iframesEl = document.querySelector( - `[data-element="js-view-iframes"]` - ); + const iframesEl = document.querySelector(`[data-el-js-view-iframes]`); initializeIframeSource(this.iframe, this.props.iframePort).then(() => { iframesEl.appendChild(this.iframe); }); diff --git a/assets/js/hooks/session.js b/assets/js/hooks/session.js index 34e17c13f..f674c9f05 100644 --- a/assets/js/hooks/session.js +++ b/assets/js/hooks/session.js @@ -405,7 +405,7 @@ const Session = { */ handleDocumentMouseDown(event) { // If the click is outside the notebook element, keep the focus as is - if (!event.target.closest(`[data-element="notebook"]`)) { + if (!event.target.closest(`[data-el-notebook]`)) { if (this.insertMode) { this.setInsertMode(false); } @@ -413,7 +413,7 @@ const Session = { } // When clicking an insert button, keep focus and insert mode as is - if (event.target.closest(`[data-element="insert-buttons"] button`)) { + if (event.target.closest(`[data-el-insert-buttons] button`)) { return; } @@ -427,7 +427,7 @@ const Session = { } // If a cell action is clicked, keep the insert mode as is - if (event.target.closest(`[data-element="actions"]`)) { + if (event.target.closest(`[data-el-actions]`)) { return; } @@ -440,7 +440,7 @@ const Session = { editableElementClicked(event, focusableEl) { if (focusableEl) { const editableElement = event.target.closest( - `[data-element="editor-container"], [data-element="heading"]` + `[data-el-editor-container], [data-el-heading]` ); return editableElement && focusableEl.contains(editableElement); } @@ -468,7 +468,7 @@ const Session = { * Enters insert mode when markdown edit action is clicked. */ handleDocumentClick(event) { - if (event.target.closest(`[data-element="enable-insert-mode-button"]`)) { + if (event.target.closest(`[data-el-enable-insert-mode-button]`)) { this.setInsertMode(true); } }, @@ -477,7 +477,7 @@ const Session = { * Enters insert mode when a markdown cell is double-clicked. */ handleDocumentDoubleClick(event) { - const cell = event.target.closest(`[data-element="cell"]`); + const cell = event.target.closest(`[data-el-cell]`); const type = cell && cell.getAttribute("data-type"); if ( @@ -494,9 +494,7 @@ const Session = { * Handles section link clicks in the section list. */ handleSectionsListClick(event) { - const sectionButton = event.target.closest( - `[data-element="sections-list-item"]` - ); + const sectionButton = event.target.closest(`[data-el-sections-list-item]`); if (sectionButton) { const sectionId = sectionButton.getAttribute("data-section-id"); const section = this.getSectionById(sectionId); @@ -508,20 +506,18 @@ const Session = { * Handles client link clicks in the clients list. */ handleClientsListClick(event) { - const clientListItem = event.target.closest( - `[data-element="clients-list-item"]` - ); + const clientListItem = event.target.closest(`[data-el-clients-list-item]`); if (clientListItem) { const clientPid = clientListItem.getAttribute("data-client-pid"); - const clientLink = event.target.closest(`[data-element="client-link"]`); + const clientLink = event.target.closest(`[data-el-client-link]`); if (clientLink) { this.handleClientLinkClick(clientPid); } const clientFollowToggle = event.target.closest( - `[data-element="client-follow-toggle"]` + `[data-el-client-follow-toggle]` ); if (clientFollowToggle) { this.handleClientFollowToggleClick(clientPid, clientListItem); @@ -535,7 +531,7 @@ const Session = { handleClientFollowToggleClick(clientPid, clientListItem) { const followedClientListItem = this.el.querySelector( - `[data-element="clients-list-item"][data-js-followed]` + `[data-el-clients-list-item][data-js-followed]` ); if (followedClientListItem) { @@ -563,7 +559,7 @@ const Session = { * Handles button clicks within cell indicators section. */ handleCellIndicatorsClick(event) { - const button = event.target.closest(`[data-element="focus-cell-button"]`); + const button = event.target.closest(`[data-el-focus-cell-button]`); if (button) { const cellId = button.getAttribute("data-target"); this.setFocusedEl(cellId); @@ -603,7 +599,7 @@ const Session = { */ updateSectionListHighlight() { const currentListItem = this.el.querySelector( - `[data-element="sections-list-item"][data-js-is-viewed]` + `[data-el-sections-list-item][data-js-is-viewed]` ); if (currentListItem) { @@ -622,7 +618,7 @@ const Session = { if (viewedSection) { const sectionId = viewedSection.getAttribute("data-section-id"); const listItem = this.el.querySelector( - `[data-element="sections-list-item"][data-section-id="${sectionId}"]` + `[data-el-sections-list-item][data-section-id="${sectionId}"]` ); listItem.setAttribute("data-js-is-viewed", ""); } @@ -651,7 +647,8 @@ const Session = { }, showBin() { - this.pushEvent("show_bin", {}); + const actionEl = this.el.querySelector(`[data-btn-show-bin]`); + actionEl && actionEl.click(); }, showDependencySearch() { @@ -715,7 +712,8 @@ const Session = { }, showShortcuts() { - this.pushEvent("show_shortcuts", {}); + const actionEl = this.el.querySelector(`[data-btn-show-shortcuts]`); + actionEl && actionEl.click(); }, isInsertModeAvailable() { @@ -803,8 +801,8 @@ const Session = { if (focusElement) { // Focus the primary content in the focusable element, this is important for screen readers const contentEl = - el.querySelector(`[data-element="cell-body"]`) || - el.querySelector(`[data-element="heading"]`) || + el.querySelector(`[data-el-cell-body]`) || + el.querySelector(`[data-el-heading]`) || el; contentEl.focus({ preventScroll: true }); } @@ -866,9 +864,7 @@ const Session = { handleSectionInserted(sectionId) { const section = this.getSectionById(sectionId); - const headlineEl = section.querySelector( - `[data-element="section-headline"]` - ); + const headlineEl = section.querySelector(`[data-el-section-headline]`); const { focusableId } = headlineEl.dataset; this.setFocusedEl(focusableId); this.setInsertMode(true); @@ -1081,7 +1077,7 @@ const Session = { getSectionIdByFocusableId(focusableId) { const el = this.getFocusableEl(focusableId); - const section = el.closest(`[data-element="section"]`); + const section = el.closest(`[data-el-section]`); return section && section.getAttribute("data-section-id"); }, @@ -1091,17 +1087,17 @@ const Session = { }, getSections() { - return Array.from(this.el.querySelectorAll(`[data-element="section"]`)); + return Array.from(this.el.querySelectorAll(`[data-el-section]`)); }, getSectionById(sectionId) { return this.el.querySelector( - `[data-element="section"][data-section-id="${sectionId}"]` + `[data-el-section][data-section-id="${sectionId}"]` ); }, getElement(name) { - return this.el.querySelector(`[data-element="${name}"]`); + return this.el.querySelector(`[data-el-${name}]`); }, }; diff --git a/lib/livebook/runtime.ex b/lib/livebook/runtime.ex index 6dbfac708..f9369bf2f 100644 --- a/lib/livebook/runtime.ex +++ b/lib/livebook/runtime.ex @@ -370,8 +370,8 @@ defprotocol Livebook.Runtime do The given `base_locator` idenfities an evaluation that may be used as the context when resolving the request (if relevant). """ - @spec handle_intellisense(t(), pid(), reference(), intellisense_request(), locator()) :: :ok - def handle_intellisense(runtime, send_to, ref, request, base_locator) + @spec handle_intellisense(t(), pid(), intellisense_request(), locator()) :: reference() + def handle_intellisense(runtime, send_to, request, base_locator) @doc """ Returns true if the given runtime is self-contained. diff --git a/lib/livebook/runtime/attached.ex b/lib/livebook/runtime/attached.ex index 76fefbdb6..4b55020f3 100644 --- a/lib/livebook/runtime/attached.ex +++ b/lib/livebook/runtime/attached.ex @@ -94,8 +94,8 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.Attached do RuntimeServer.drop_container(runtime.server_pid, container_ref) end - def handle_intellisense(runtime, send_to, ref, request, base_locator) do - RuntimeServer.handle_intellisense(runtime.server_pid, send_to, ref, request, base_locator) + def handle_intellisense(runtime, send_to, request, base_locator) do + RuntimeServer.handle_intellisense(runtime.server_pid, send_to, request, base_locator) end def standalone?(_runtime), do: false diff --git a/lib/livebook/runtime/elixir_standalone.ex b/lib/livebook/runtime/elixir_standalone.ex index f340f6bb8..1b7aa9785 100644 --- a/lib/livebook/runtime/elixir_standalone.ex +++ b/lib/livebook/runtime/elixir_standalone.ex @@ -141,8 +141,8 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.ElixirStandalone do RuntimeServer.drop_container(runtime.server_pid, container_ref) end - def handle_intellisense(runtime, send_to, ref, request, base_locator) do - RuntimeServer.handle_intellisense(runtime.server_pid, send_to, ref, request, base_locator) + def handle_intellisense(runtime, send_to, request, base_locator) do + RuntimeServer.handle_intellisense(runtime.server_pid, send_to, request, base_locator) end def standalone?(_runtime), do: true diff --git a/lib/livebook/runtime/embedded.ex b/lib/livebook/runtime/embedded.ex index 6b8dbdce0..4cbe7804f 100644 --- a/lib/livebook/runtime/embedded.ex +++ b/lib/livebook/runtime/embedded.ex @@ -88,8 +88,8 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.Embedded do RuntimeServer.drop_container(runtime.server_pid, container_ref) end - def handle_intellisense(runtime, send_to, ref, request, base_locator) do - RuntimeServer.handle_intellisense(runtime.server_pid, send_to, ref, request, base_locator) + def handle_intellisense(runtime, send_to, request, base_locator) do + RuntimeServer.handle_intellisense(runtime.server_pid, send_to, request, base_locator) end def standalone?(_runtime), do: false diff --git a/lib/livebook/runtime/erl_dist/runtime_server.ex b/lib/livebook/runtime/erl_dist/runtime_server.ex index 46e24d089..1b9bdd102 100644 --- a/lib/livebook/runtime/erl_dist/runtime_server.ex +++ b/lib/livebook/runtime/erl_dist/runtime_server.ex @@ -108,12 +108,13 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do @spec handle_intellisense( pid(), pid(), - reference(), Runtime.intellisense_request(), Runtime.locator() - ) :: :ok - def handle_intellisense(pid, send_to, ref, request, base_locator) do + ) :: reference() + def handle_intellisense(pid, send_to, request, base_locator) do + ref = make_ref() GenServer.cast(pid, {:handle_intellisense, send_to, ref, request, base_locator}) + ref end @doc """ diff --git a/lib/livebook/runtime/mix_standalone.ex b/lib/livebook/runtime/mix_standalone.ex index e3fc247d8..77045227b 100644 --- a/lib/livebook/runtime/mix_standalone.ex +++ b/lib/livebook/runtime/mix_standalone.ex @@ -181,8 +181,8 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.MixStandalone do RuntimeServer.drop_container(runtime.server_pid, container_ref) end - def handle_intellisense(runtime, send_to, ref, request, base_locator) do - RuntimeServer.handle_intellisense(runtime.server_pid, send_to, ref, request, base_locator) + def handle_intellisense(runtime, send_to, request, base_locator) do + RuntimeServer.handle_intellisense(runtime.server_pid, send_to, request, base_locator) end def standalone?(_runtime), do: true diff --git a/lib/livebook_web/live/home_live.ex b/lib/livebook_web/live/home_live.ex index 91fbd7cce..df129074e 100644 --- a/lib/livebook_web/live/home_live.ex +++ b/lib/livebook_web/live/home_live.ex @@ -85,7 +85,7 @@ defmodule LivebookWeb.HomeLive do -
+

Explore diff --git a/lib/livebook_web/live/home_live/session_list_component.ex b/lib/livebook_web/live/home_live/session_list_component.ex index c384528ac..9b28de388 100644 --- a/lib/livebook_web/live/home_live/session_list_component.ex +++ b/lib/livebook_web/live/home_live/session_list_component.ex @@ -109,7 +109,7 @@ defmodule LivebookWeb.HomeLive.SessionListComponent do

@@ -218,7 +218,7 @@ defmodule LivebookWeb.HomeLive.SessionListComponent do Edit diff --git a/lib/livebook_web/live/output/text_component.ex b/lib/livebook_web/live/output/text_component.ex index b2b758101..d761fd033 100644 --- a/lib/livebook_web/live/output/text_component.ex +++ b/lib/livebook_web/live/output/text_component.ex @@ -17,7 +17,7 @@ defmodule LivebookWeb.Output.TextComponent do phx-update="ignore">
diff --git a/lib/livebook_web/live/session_live.ex b/lib/livebook_web/live/session_live.ex index 0ca8f4342..6256c5cc8 100644 --- a/lib/livebook_web/live/session_live.ex +++ b/lib/livebook_web/live/session_live.ex @@ -90,7 +90,7 @@ defmodule LivebookWeb.SessionLive do ~H"""
@@ -99,45 +99,47 @@ defmodule LivebookWeb.SessionLive do + button_attrs={[data_el_sections_list_toggle: true]} /> + button_attrs={[data_el_clients_list_toggle: true]} /> + button_attrs={[data_el_runtime_info_toggle: true]} /> + active={@live_action == :bin} + link_attrs={[data_btn_show_bin: true]} /> + active={@live_action == :shortcuts} + link_attrs={[data_btn_show_shortcuts: true]} />
-
+ data-el-side-panel> +
<.sections_list data_view={@data_view} />
-
+
<.clients_list data_view={@data_view} self={@self} />
-
+
<.runtime_info data_view={@data_view} session={@session} socket={@socket} />
-
-
-
+
+
+
<%= @data_view.notebook_name %> <.menu id="session-menu"> <:toggle> @@ -337,7 +339,7 @@ defmodule LivebookWeb.SessionLive do <%= for section_item <- @data_view.sections_items do %>
<%= if client_pid != @self do %> @@ -510,7 +512,7 @@ defmodule LivebookWeb.SessionLive do defp session_status(%{status: :stale} = assigns) do ~H""" - @@ -782,16 +784,6 @@ defmodule LivebookWeb.SessionLive do end end - def handle_event("show_shortcuts", %{}, socket) do - {:noreply, - push_patch(socket, to: Routes.session_path(socket, :shortcuts, socket.assigns.session.id))} - end - - def handle_event("show_bin", %{}, socket) do - {:noreply, - push_patch(socket, to: Routes.session_path(socket, :bin, socket.assigns.session.id))} - end - def handle_event("reconnect_runtime", %{}, socket) do {_, socket} = maybe_reconnect_runtime(socket) {:noreply, socket} @@ -838,9 +830,8 @@ defmodule LivebookWeb.SessionLive do with {:ok, cell, section} <- Notebook.fetch_cell_and_section(data.notebook, cell_id) do if Runtime.connected?(data.runtime) do - ref = make_ref() base_locator = Session.find_base_locator(data, cell, section, existing: true) - Runtime.handle_intellisense(data.runtime, self(), ref, request, base_locator) + ref = Runtime.handle_intellisense(data.runtime, self(), request, base_locator) {:reply, %{"ref" => inspect(ref)}, socket} else diff --git a/lib/livebook_web/live/session_live/cell_component.ex b/lib/livebook_web/live/session_live/cell_component.ex index b4af60296..8d598be15 100644 --- a/lib/livebook_web/live/session_live/cell_component.ex +++ b/lib/livebook_web/live/session_live/cell_component.ex @@ -5,7 +5,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do def render(assigns) do ~H"""
<.cell_body> -
+
<.live_component module={LivebookWeb.SessionLive.CellEditorComponent} id={"#{@cell_view.id}-primary"} cell_id={@cell_view.id} @@ -42,7 +42,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do language="markdown" />
<.content_skeleton empty={empty?(@cell_view.source_view)} /> @@ -111,13 +111,13 @@ defmodule LivebookWeb.SessionLive.CellComponent do <.cell_body> -
+
Notebook dependencies and setup <.cell_status id={"#{@cell_view.id}-1"} cell_view={@cell_view} />
-
+
<.live_component module={LivebookWeb.SessionLive.CellEditorComponent} id={"#{@cell_view.id}-primary"} @@ -162,7 +162,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do <.cell_body> -
+
<%= case @cell_view.status do %> <% :started -> %>
@@ -195,7 +195,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do <.cell_status id={"#{@cell_view.id}-1"} cell_view={@cell_view} />
-
+
<.live_component module={LivebookWeb.SessionLive.CellEditorComponent} id={"#{@cell_view.id}-primary"} @@ -227,13 +227,13 @@ defmodule LivebookWeb.SessionLive.CellComponent do ~H"""
-
+
<%= render_slot(@primary) %>
@@ -244,8 +244,8 @@ defmodule LivebookWeb.SessionLive.CellComponent do ~H""" -
-
+
+
<%= render_slot(@inner_block) %> @@ -324,7 +324,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do defp enable_insert_mode_button(assigns) do ~H""" - + @@ -334,7 +334,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do defp insert_image_button(assigns) do ~H""" - + <%= live_patch to: Routes.session_path(@socket, :cell_upload, @session_id, @cell_id), class: "icon-button", aria_label: "insert image", @@ -347,10 +347,10 @@ defmodule LivebookWeb.SessionLive.CellComponent do defp toggle_source_button(assigns) do ~H""" - + """ @@ -414,11 +414,11 @@ defmodule LivebookWeb.SessionLive.CellComponent do def amplify_output_button(assigns) do ~H""" - + """ @@ -488,7 +488,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do defp setup_cell_info(assigns) do ~H""" -
-
+
<%= render_slot(@inner_block) %> <%= if @change_indicator do %> - * + * <% end %>
diff --git a/lib/livebook_web/live/session_live/cell_editor_component.ex b/lib/livebook_web/live/session_live/cell_editor_component.ex index accd64e13..b4efbf986 100644 --- a/lib/livebook_web/live/session_live/cell_editor_component.ex +++ b/lib/livebook_web/live/session_live/cell_editor_component.ex @@ -42,7 +42,7 @@ defmodule LivebookWeb.SessionLive.CellEditorComponent do phx-hook="CellEditor" data-cell-id={@cell_id} data-tag={@tag}> -
+
<.content_skeleton bg_class="bg-gray-500" empty={empty?(@source_view)} />
diff --git a/lib/livebook_web/live/session_live/indicators_component.ex b/lib/livebook_web/live/session_live/indicators_component.ex index a16c9651b..34a1e86e1 100644 --- a/lib/livebook_web/live/session_live/indicators_component.ex +++ b/lib/livebook_web/live/session_live/indicators_component.ex @@ -4,7 +4,7 @@ defmodule LivebookWeb.SessionLive.IndicatorsComponent do @impl true def render(assigns) do ~H""" -
+
<%= if @file do %> <%= if @dirty do %> <%= if @autosave_interval_s do %> @@ -58,7 +58,7 @@ defmodule LivebookWeb.SessionLive.IndicatorsComponent do <% end %> <%# Note: this indicator is shown/hidden using CSS based on the current mode %> - + ins @@ -72,7 +72,7 @@ defmodule LivebookWeb.SessionLive.IndicatorsComponent do @@ -85,7 +85,7 @@ defmodule LivebookWeb.SessionLive.IndicatorsComponent do @@ -98,7 +98,7 @@ defmodule LivebookWeb.SessionLive.IndicatorsComponent do diff --git a/lib/livebook_web/live/session_live/insert_buttons_component.ex b/lib/livebook_web/live/session_live/insert_buttons_component.ex index 786362a93..f2ace4a87 100644 --- a/lib/livebook_web/live/session_live/insert_buttons_component.ex +++ b/lib/livebook_web/live/session_live/insert_buttons_component.ex @@ -6,7 +6,7 @@ defmodule LivebookWeb.SessionLive.InsertButtonsComponent do