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
This commit is contained in:
Jonatan Kłosko 2022-04-04 12:19:11 +02:00 committed by GitHub
parent 00c913adcc
commit 902c993098
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 256 additions and 275 deletions

View file

@ -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

View file

@ -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;
}

View file

@ -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.";
}

View file

@ -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();

View file

@ -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

View file

@ -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;

View file

@ -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);
});

View file

@ -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}]`);
},
};

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 """

View file

@ -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

View file

@ -85,7 +85,7 @@ defmodule LivebookWeb.HomeLive do
</.live_component>
</div>
<div class="py-12" data-element="explore-section" role="region" aria-label="explore section">
<div class="py-12" data-el-explore-section role="region" aria-label="explore section">
<div class="mb-4 flex justify-between items-center">
<h2 class="uppercase font-semibold text-gray-500">
Explore

View file

@ -109,7 +109,7 @@ defmodule LivebookWeb.HomeLive.SessionListComponent do
<input type="checkbox" name="session_ids[]" value={session.id}
aria-label={session.notebook_name}
class="checkbox-base hidden mr-3"
data-element="bulk-edit-member"
data-el-bulk-edit-member
phx-click={JS.dispatch("lb:session_list:on_selection_change")}>
</div>
<div class="grow flex flex-col items-start">
@ -218,7 +218,7 @@ defmodule LivebookWeb.HomeLive.SessionListComponent do
<span>Edit</span>
</button>
<button class="hidden w-28 button-base button-outlined-gray px-4 py-1 flex justify-between items-center"
data-element="bulk-edit-member"
data-el-bulk-edit-member
type="button">
<span>Actions</span>
<.remix_icon icon="arrow-down-s-line" class="text-lg leading-none align-middle ml-1" />
@ -264,14 +264,14 @@ defmodule LivebookWeb.HomeLive.SessionListComponent do
end
def toggle_edit(:on) do
JS.remove_class("hidden", to: "[data-element='bulk-edit-member']")
JS.remove_class("hidden", to: "[data-el-bulk-edit-member]")
|> JS.add_class("hidden", to: "#toggle-edit")
|> JS.dispatch("lb:session_list:on_selection_change")
|> sr_message("bulk actions available")
end
def toggle_edit(:off) do
JS.add_class("hidden", to: "[data-element='bulk-edit-member']")
JS.add_class("hidden", to: "[data-el-bulk-edit-member]")
|> JS.remove_class("hidden", to: "#toggle-edit")
|> JS.dispatch("lb:uncheck", to: "[name='session_ids[]']")
|> JS.dispatch("lb:session_list:on_selection_change")

View file

@ -12,7 +12,7 @@ defmodule LivebookWeb.Output do
~H"""
<%= for {idx, output} <- Enum.reverse(@outputs) do %>
<div class="max-w-full" id={"output-wrapper-#{@dom_id_map[idx] || idx}"}
data-element="output"
data-el-output
data-border={border?(output)}
data-wrapper={wrapper?(output)}>
<%= render_output(output, %{

View file

@ -45,7 +45,7 @@ defmodule LivebookWeb.Output.InputComponent do
defp input(%{attrs: %{type: :select}} = assigns) do
~H"""
<select
data-element="input"
data-el-input
class="input input-select"
name="value">
<%= for {{key, label}, idx} <- Enum.with_index(@attrs.options) do %>
@ -61,7 +61,7 @@ defmodule LivebookWeb.Output.InputComponent do
~H"""
<div class="mt-1">
<.switch_checkbox
data-element="input"
data-el-input
name="value"
checked={@value} />
</div>
@ -73,7 +73,7 @@ defmodule LivebookWeb.Output.InputComponent do
<div class="flex items-center space-x-2">
<div><%= @attrs.min %></div>
<input type="range"
data-element="input"
data-el-input
class="input-range"
name="value"
value={@value}
@ -93,7 +93,7 @@ defmodule LivebookWeb.Output.InputComponent do
defp input(%{attrs: %{type: :textarea}} = assigns) do
~H"""
<textarea
data-element="input"
data-el-input
class="input min-h-[200px] tiny-scrollbar"
name="value"
phx-debounce="300"
@ -107,7 +107,7 @@ defmodule LivebookWeb.Output.InputComponent do
~H"""
<.with_password_toggle id={"#{@id}-password-toggle"}>
<input type="password"
data-element="input"
data-el-input
class="input w-auto bg-gray-50"
name="value"
value={@value}
@ -123,7 +123,7 @@ defmodule LivebookWeb.Output.InputComponent do
defp input(%{attrs: %{type: type}} = assigns) when type in [:number, :color, :url, :text] do
~H"""
<input type={html_input_type(@attrs.type)}
data-element="input"
data-el-input
class={"input w-auto #{if(@error, do: "input--error")}"}
name="value"
value={to_string(@value)}

View file

@ -65,7 +65,7 @@ defmodule LivebookWeb.Output.StdoutComponent do
phx-update="ignore"></div>
<div class="absolute right-2 top-0 z-10">
<button class="icon-button bg-gray-100"
data-element="clipcopy"
data-el-clipcopy
phx-click={JS.dispatch("lb:clipcopy", to: "#virtualized-text-#{@id}-template")}>
<.remix_icon icon="clipboard-line" class="text-lg" />
</button>

View file

@ -17,7 +17,7 @@ defmodule LivebookWeb.Output.TextComponent do
phx-update="ignore"></div>
<div class="absolute right-2 top-0 z-10">
<button class="icon-button bg-gray-100"
data-element="clipcopy"
data-el-clipcopy
phx-click={JS.dispatch("lb:clipcopy", to: "#virtualized-text-#{@id}-template")}>
<.remix_icon icon="clipboard-line" class="text-lg" />
</button>

View file

@ -90,7 +90,7 @@ defmodule LivebookWeb.SessionLive do
~H"""
<div class="flex grow h-full"
id={"session-#{@session.id}"}
data-element="session"
data-el-session
phx-hook="Session"
data-global-status={elem(@data_view.global_status, 0)}
data-autofocus-cell-id={@autofocus_cell_id}>
@ -99,45 +99,47 @@ defmodule LivebookWeb.SessionLive do
<SidebarHelpers.button_item
icon="booklet-fill"
label="Sections (ss)"
data_element="sections-list-toggle" />
button_attrs={[data_el_sections_list_toggle: true]} />
<SidebarHelpers.button_item
icon="group-fill"
label="Connected users (su)"
data_element="clients-list-toggle" />
button_attrs={[data_el_clients_list_toggle: true]} />
<SidebarHelpers.button_item
icon="cpu-line"
label="Runtime settings (sr)"
data_element="runtime-info-toggle" />
button_attrs={[data_el_runtime_info_toggle: true]} />
<SidebarHelpers.link_item
icon="delete-bin-6-fill"
label="Bin (sb)"
path={Routes.session_path(@socket, :bin, @session.id)}
active={@live_action == :bin} />
active={@live_action == :bin}
link_attrs={[data_btn_show_bin: true]} />
<SidebarHelpers.break_item />
<SidebarHelpers.link_item
icon="keyboard-box-fill"
label="Keyboard shortcuts (?)"
path={Routes.session_path(@socket, :shortcuts, @session.id)}
active={@live_action == :shortcuts} />
active={@live_action == :shortcuts}
link_attrs={[data_btn_show_shortcuts: true]} />
<SidebarHelpers.user_item current_user={@current_user} />
</SidebarHelpers.sidebar>
<div class="flex flex-col h-full w-full max-w-xs absolute z-30 top-0 left-[64px] overflow-y-auto shadow-xl md:static md:shadow-none bg-gray-50 border-r border-gray-100 px-6 py-10"
data-element="side-panel">
<div data-element="sections-list">
data-el-side-panel>
<div data-el-sections-list>
<.sections_list data_view={@data_view} />
</div>
<div data-element="clients-list">
<div data-el-clients-list>
<.clients_list data_view={@data_view} self={@self} />
</div>
<div data-element="runtime-info">
<div data-el-runtime-info>
<.runtime_info data_view={@data_view} session={@session} socket={@socket} />
</div>
</div>
<div class="grow overflow-y-auto relative" data-element="notebook">
<div data-element="js-view-iframes" phx-update="ignore" id="js-view-iframes"></div>
<div class="w-full max-w-screen-lg px-16 mx-auto py-7" data-element="notebook-content">
<div class="grow overflow-y-auto relative" data-el-notebook>
<div data-el-js-view-iframes phx-update="ignore" id="js-view-iframes"></div>
<div class="w-full max-w-screen-lg px-16 mx-auto py-7" data-el-notebook-content>
<div class="flex items-center pb-4 mb-2 space-x-4 border-b border-gray-200"
data-element="notebook-headline"
data-el-notebook-headline
data-focusable-id="notebook"
id="notebook"
phx-hook="Headline"
@ -146,7 +148,7 @@ defmodule LivebookWeb.SessionLive do
<h1 class="grow p-1 -ml-1 text-3xl font-semibold text-gray-800 border border-transparent rounded-lg whitespace-pre-wrap"
tabindex="0"
id="notebook-heading"
data-element="heading"
data-el-heading
spellcheck="false"><%= @data_view.notebook_name %></h1>
<.menu id="session-menu">
<:toggle>
@ -337,7 +339,7 @@ defmodule LivebookWeb.SessionLive do
<%= for section_item <- @data_view.sections_items do %>
<div class="flex items-center">
<button class="grow flex items-center text-gray-500 hover:text-gray-900 text-left"
data-element="sections-list-item"
data-el-sections-list-item
data-section-id={section_item.id}>
<span class="flex items-center space-x-1">
<span><%= section_item.name %></span>
@ -379,24 +381,24 @@ defmodule LivebookWeb.SessionLive do
<%= for {client_pid, user} <- @data_view.clients do %>
<div class="flex items-center justify-between space-x-2"
id={"clients-list-item-#{inspect(client_pid)}"}
data-element="clients-list-item"
data-el-clients-list-item
data-client-pid={inspect(client_pid)}>
<button class="flex items-center space-x-2 text-gray-500 hover:text-gray-900 disabled:pointer-events-none"
disabled={client_pid == @self}
data-element="client-link">
data-el-client-link>
<.user_avatar user={user} class="shrink-0 h-7 w-7" text_class="text-xs" />
<span><%= user.name || "Anonymous" %></span>
</button>
<%= if client_pid != @self do %>
<span class="tooltip left" data-tooltip="Follow this user"
data-element="client-follow-toggle"
data-el-client-follow-toggle
data-meta="follow">
<button class="icon-button" aria-label="follow this user">
<.remix_icon icon="pushpin-line" class="text-lg" />
</button>
</span>
<span class="tooltip left" data-tooltip="Unfollow this user"
data-element="client-follow-toggle"
data-el-client-follow-toggle
data-meta="unfollow">
<button class="icon-button" aria-label="unfollow this user">
<.remix_icon icon="pushpin-fill" class="text-lg" />
@ -501,7 +503,7 @@ defmodule LivebookWeb.SessionLive do
defp session_status(%{status: :evaluating} = assigns) do
~H"""
<button data-element="focus-cell-button" data-target={@cell_id}>
<button data-el-focus-cell-button data-target={@cell_id}>
<.status_indicator circle_class="bg-blue-500" animated_circle_class="bg-blue-400">
</.status_indicator>
</button>
@ -510,7 +512,7 @@ defmodule LivebookWeb.SessionLive do
defp session_status(%{status: :stale} = assigns) do
~H"""
<button data-element="focus-cell-button" data-target={@cell_id}>
<button data-el-focus-cell-button data-target={@cell_id}>
<.status_indicator circle_class="bg-yellow-bright-200">
</.status_indicator>
</button>
@ -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

View file

@ -5,7 +5,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
def render(assigns) do
~H"""
<div class="flex flex-col relative"
data-element="cell"
data-el-cell
id={"cell-#{@cell_view.id}"}
phx-hook="Cell"
data-cell-id={@cell_view.id}
@ -33,7 +33,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
</:secondary>
</.cell_actions>
<.cell_body>
<div class="pb-4" data-element="editor-box">
<div class="pb-4" data-el-editor-box>
<.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" />
</div>
<div class="markdown"
data-element="markdown-container"
data-el-markdown-container
id={"markdown-container-#{@cell_view.id}"}
phx-update="ignore">
<.content_skeleton empty={empty?(@cell_view.source_view)} />
@ -111,13 +111,13 @@ defmodule LivebookWeb.SessionLive.CellComponent do
</:secondary>
</.cell_actions>
<.cell_body>
<div data-element="info-box">
<div data-el-info-box>
<div class="p-3 flex items-center justify-between border border-gray-200 text-sm text-gray-400 font-medium rounded-lg">
<span>Notebook dependencies and setup</span>
<.cell_status id={"#{@cell_view.id}-1"} cell_view={@cell_view} />
</div>
</div>
<div data-element="editor-box">
<div data-el-editor-box>
<div class="relative">
<.live_component module={LivebookWeb.SessionLive.CellEditorComponent}
id={"#{@cell_view.id}-primary"}
@ -162,7 +162,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
</:secondary>
</.cell_actions>
<.cell_body>
<div data-element="ui-box">
<div data-el-ui-box>
<%= case @cell_view.status do %>
<% :started -> %>
<div class={"flex #{if(@cell_view.editor && @cell_view.editor.placement == :top, do: "flex-col-reverse", else: "flex-col")}"}>
@ -195,7 +195,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
<.cell_status id={"#{@cell_view.id}-1"} cell_view={@cell_view} />
</div>
</div>
<div data-element="editor-box">
<div data-el-editor-box>
<div class="relative">
<.live_component module={LivebookWeb.SessionLive.CellEditorComponent}
id={"#{@cell_view.id}-primary"}
@ -227,13 +227,13 @@ defmodule LivebookWeb.SessionLive.CellComponent do
~H"""
<div class="mb-1 flex items-center justify-between">
<div class="relative z-20 flex items-center justify-end space-x-2" data-element="actions" data-primary>
<div class="relative z-20 flex items-center justify-end space-x-2" data-el-actions data-primary>
<%= render_slot(@primary) %>
</div>
<div class="relative z-20 flex items-center justify-end space-x-2"
role="toolbar"
aria-label="cell actions"
data-element="actions">
data-el-actions>
<%= render_slot(@secondary) %>
</div>
</div>
@ -244,8 +244,8 @@ defmodule LivebookWeb.SessionLive.CellComponent do
~H"""
<!-- By setting tabindex we can programmatically focus this element,
also we actually want to make this element tab-focusable -->
<div class="flex relative" data-element="cell-body" tabindex="0">
<div class="w-1 h-full rounded-lg absolute top-0 -left-3" data-element="cell-focus-indicator">
<div class="flex relative" data-el-cell-body tabindex="0">
<div class="w-1 h-full rounded-lg absolute top-0 -left-3" data-el-cell-focus-indicator>
</div>
<div class="w-full">
<%= render_slot(@inner_block) %>
@ -324,7 +324,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
defp enable_insert_mode_button(assigns) do
~H"""
<span class="tooltip top" data-tooltip="Edit content" data-element="enable-insert-mode-button">
<span class="tooltip top" data-tooltip="Edit content" data-el-enable-insert-mode-button>
<button class="icon-button" aria-label="edit content">
<.remix_icon icon="pencil-line" class="text-xl" />
</button>
@ -334,7 +334,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
defp insert_image_button(assigns) do
~H"""
<span class="tooltip top" data-tooltip="Insert image" data-element="insert-image-button">
<span class="tooltip top" data-tooltip="Insert image" data-el-insert-image-button>
<%= 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"""
<span class="tooltip top" data-tooltip="Toggle source" data-element="toggle-source-button">
<span class="tooltip top" data-tooltip="Toggle source" data-el-toggle-source-button>
<button class="icon-button" aria-label="toggle source">
<.remix_icon icon="code-line" class="text-xl" data-element="show-code-icon" />
<.remix_icon icon="pencil-line" class="text-xl" data-element="show-ui-icon" />
<.remix_icon icon="code-line" class="text-xl" data-el-show-code-icon />
<.remix_icon icon="pencil-line" class="text-xl" data-el-show-ui-icon />
</button>
</span>
"""
@ -414,11 +414,11 @@ defmodule LivebookWeb.SessionLive.CellComponent do
def amplify_output_button(assigns) do
~H"""
<span class="tooltip top" data-tooltip="Amplify output" data-element="amplify-outputs-button">
<span class="tooltip top" data-tooltip="Amplify output" data-el-amplify-outputs-button>
<button class="icon-button"
aria-label="amplify outputs">
<.remix_icon icon="zoom-in-line" class="text-xl" data-element="zoom-in-icon" />
<.remix_icon icon="zoom-out-line" class="text-xl" data-element="zoom-out-icon" />
<.remix_icon icon="zoom-in-line" class="text-xl" data-el-zoom-in-icon />
<.remix_icon icon="zoom-out-line" class="text-xl" data-el-zoom-out-icon />
</button>
</span>
"""
@ -488,7 +488,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
defp setup_cell_info(assigns) do
~H"""
<span class="tooltip top"
<span class="tooltip left"
data-tooltip={
~s'''
The setup cell includes code that initializes the notebook
@ -506,7 +506,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
defp evaluation_outputs(assigns) do
~H"""
<div class="flex flex-col"
data-element="outputs-container"
data-el-outputs-container
id={"outputs-#{@cell_view.id}-#{@cell_view.eval.outputs_batch_number}"}
phx-update="append">
<LivebookWeb.Output.outputs
@ -584,10 +584,10 @@ defmodule LivebookWeb.SessionLive.CellComponent do
~H"""
<div class={"#{if(@tooltip, do: "tooltip")} bottom distant-medium"} data-tooltip={@tooltip}>
<div class="flex items-center space-x-1">
<div class="flex text-xs text-gray-400" data-element="cell-status">
<div class="flex text-xs text-gray-400" data-el-cell-status>
<%= render_slot(@inner_block) %>
<%= if @change_indicator do %>
<span data-element="change-indicator">*</span>
<span data-el-change-indicator>*</span>
<% end %>
</div>
<span class="flex relative h-3 w-3">

View file

@ -42,7 +42,7 @@ defmodule LivebookWeb.SessionLive.CellEditorComponent do
phx-hook="CellEditor"
data-cell-id={@cell_id}
data-tag={@tag}>
<div class="py-3 rounded-lg bg-editor" data-element="editor-container">
<div class="py-3 rounded-lg bg-editor" data-el-editor-container>
<div class="px-8">
<.content_skeleton bg_class="bg-gray-500" empty={empty?(@source_view)} />
</div>

View file

@ -4,7 +4,7 @@ defmodule LivebookWeb.SessionLive.IndicatorsComponent do
@impl true
def render(assigns) do
~H"""
<div class="flex flex-col items-center space-y-2" data-element="notebook-indicators">
<div class="flex flex-col items-center space-y-2" data-el-notebook-indicators>
<%= 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 %>
<span class="tooltip left" data-tooltip="Insert mode" data-element="insert-mode-indicator">
<span class="tooltip left" data-tooltip="Insert mode" data-el-insert-mode-indicator>
<span class="text-sm font-medium text-gray-400 cursor-default">
ins
</span>
@ -72,7 +72,7 @@ defmodule LivebookWeb.SessionLive.IndicatorsComponent do
<span class="tooltip left" data-tooltip="Go to evaluating cell">
<button class="border-blue-400 icon-button icon-outlined-button hover:bg-blue-50 focus:bg-blue-50"
aria-label="go to evaluating cell"
data-element="focus-cell-button"
data-el-focus-cell-button
data-target={@cell_id}>
<.remix_icon icon="loader-3-line" class="text-xl text-blue-500 animate-spin" />
</button>
@ -85,7 +85,7 @@ defmodule LivebookWeb.SessionLive.IndicatorsComponent do
<span class="tooltip left" data-tooltip="Go to last evaluated cell">
<button class="border-green-bright-300 icon-button icon-outlined-button hover:bg-green-bright-50 focus:bg-green-bright-50"
aria-label="go to last evaluated cell"
data-element="focus-cell-button"
data-el-focus-cell-button
data-target={@cell_id}>
<.remix_icon icon="loader-3-line" class="text-xl text-green-bright-400" />
</button>
@ -98,7 +98,7 @@ defmodule LivebookWeb.SessionLive.IndicatorsComponent do
<span class="tooltip left" data-tooltip="Go to first stale cell">
<button class="border-yellow-bright-200 icon-button icon-outlined-button hover:bg-yellow-bright-50 focus:bg-yellow-bright-50"
aria-label="go to first stale cell"
data-element="focus-cell-button"
data-el-focus-cell-button
data-target={@cell_id}>
<.remix_icon icon="loader-3-line" class="text-xl text-yellow-bright-300" />
</button>

View file

@ -6,7 +6,7 @@ defmodule LivebookWeb.SessionLive.InsertButtonsComponent do
<div class="relative top-0.5 m-0 flex justify-center"
role="toolbar"
aria-label="insert new"
data-element="insert-buttons">
data-el-insert-buttons>
<div class={"w-full absolute z-10 focus-within:z-[11] #{if(@persistent, do: "opacity-100", else: "opacity-0")} hover:opacity-100 focus-within:opacity-100 flex space-x-2 justify-center items-center"}>
<button class="button-base button-small"
phx-click="insert_cell_below"

View file

@ -3,9 +3,9 @@ defmodule LivebookWeb.SessionLive.SectionComponent do
def render(assigns) do
~H"""
<section data-element="section" data-section-id={@section_view.id}>
<section data-el-section data-section-id={@section_view.id}>
<div class="flex space-x-4 items-center"
data-element="section-headline"
data-el-section-headline
id={@section_view.id}
data-focusable-id={@section_view.id}
phx-hook="Headline"
@ -14,9 +14,9 @@ defmodule LivebookWeb.SessionLive.SectionComponent do
<h2 class="grow text-gray-800 font-semibold text-2xl px-1 -ml-1 rounded-lg border border-transparent whitespace-pre-wrap cursor-text"
tabindex="0"
id={@section_view.html_id}
data-element="heading"
data-el-heading
spellcheck="false"><%= @section_view.name %></h2>
<div class="flex space-x-2 items-center" data-element="section-actions"
<div class="flex space-x-2 items-center" data-el-section-actions
role="toolbar"
aria-label="section actions">
<span class="tooltip top" data-tooltip="Link">

View file

@ -36,7 +36,7 @@ defmodule LivebookWeb.SidebarHelpers do
<span class="tooltip right distant" data-tooltip={@label}>
<button class="text-2xl text-gray-400 hover:text-gray-50 focus:text-gray-50 rounded-xl h-10 w-10 flex items-center justify-center"
aria-label={@label}
data-element={@data_element}>
{@button_attrs}>
<.remix_icon icon={@icon} />
</button>
</span>
@ -44,11 +44,13 @@ defmodule LivebookWeb.SidebarHelpers do
end
def link_item(assigns) do
assigns = assign_new(assigns, :link_attrs, fn -> [] end)
~H"""
<span class="tooltip right distant" data-tooltip={@label}>
<%= live_patch to: @path,
<%= live_patch [to: @path,
class: "text-gray-400 hover:text-gray-50 focus:text-gray-50 rounded-xl h-10 w-10 flex items-center justify-center #{if(@active, do: "text-gray-50 bg-gray-700")}",
aria_label: @label do %>
aria_label: @label] ++ @link_attrs do %>
<.remix_icon icon={@icon} class="text-2xl" />
<% end %>
</span>

View file

@ -135,9 +135,9 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServerTest do
describe "handle_intellisense/5 given completion request" do
test "provides basic completion when no evaluation reference is given", %{pid: pid} do
request = {:completion, "System.ver"}
RuntimeServer.handle_intellisense(pid, self(), :ref, request, {:c1, nil})
ref = RuntimeServer.handle_intellisense(pid, self(), request, {:c1, nil})
assert_receive {:runtime_intellisense_response, :ref, ^request,
assert_receive {:runtime_intellisense_response, ^ref, ^request,
%{items: [%{label: "version/0"}]}}
end
@ -151,15 +151,15 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServerTest do
assert_receive {:runtime_evaluation_response, :e1, _, %{evaluation_time_ms: _time_ms}}
request = {:completion, "num"}
RuntimeServer.handle_intellisense(pid, self(), :ref, request, {:c1, :e1})
ref = RuntimeServer.handle_intellisense(pid, self(), request, {:c1, :e1})
assert_receive {:runtime_intellisense_response, :ref, ^request,
assert_receive {:runtime_intellisense_response, ^ref, ^request,
%{items: [%{label: "number"}]}}
request = {:completion, "ANSI.brigh"}
RuntimeServer.handle_intellisense(pid, self(), :ref, request, {:c1, :e1})
ref = RuntimeServer.handle_intellisense(pid, self(), request, {:c1, :e1})
assert_receive {:runtime_intellisense_response, :ref, ^request,
assert_receive {:runtime_intellisense_response, ^ref, ^request,
%{items: [%{label: "bright/0"}]}}
end
end
@ -167,9 +167,9 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServerTest do
describe "handle_intellisense/5 given details request" do
test "responds with identifier details", %{pid: pid} do
request = {:details, "System.version", 10}
RuntimeServer.handle_intellisense(pid, self(), :ref, request, {:c1, nil})
ref = RuntimeServer.handle_intellisense(pid, self(), request, {:c1, nil})
assert_receive {:runtime_intellisense_response, :ref, ^request,
assert_receive {:runtime_intellisense_response, ^ref, ^request,
%{range: %{from: 1, to: 15}, contents: [_]}}
end
end
@ -177,9 +177,9 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServerTest do
describe "handle_intellisense/5 given format request" do
test "responds with a formatted code", %{pid: pid} do
request = {:format, "System.version"}
RuntimeServer.handle_intellisense(pid, self(), :ref, request, {:c1, nil})
ref = RuntimeServer.handle_intellisense(pid, self(), request, {:c1, nil})
assert_receive {:runtime_intellisense_response, :ref, ^request, %{code: "System.version()"}}
assert_receive {:runtime_intellisense_response, ^ref, ^request, %{code: "System.version()"}}
end
end

View file

@ -233,7 +233,7 @@ defmodule LivebookWeb.HomeLiveTest do
assert {:error, {:live_redirect, %{to: to}}} =
view
|> element(~s{[data-element="explore-section"] a}, "Welcome to Livebook")
|> element(~s{[data-el-explore-section] a}, "Welcome to Livebook")
|> render_click()
|> follow_redirect(conn)

View file

@ -123,7 +123,7 @@ defmodule LivebookWeb.SessionLiveTest do
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
view
|> element(~s{[data-element="session"]})
|> element(~s{[data-el-session]})
|> render_hook("queue_cell_evaluation", %{"cell_id" => cell_id})
assert %{cell_infos: %{^cell_id => %{eval: %{status: :evaluating}}}} =
@ -137,7 +137,7 @@ defmodule LivebookWeb.SessionLiveTest do
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
view
|> element(~s{[data-element="session"]})
|> element(~s{[data-el-session]})
|> render_hook("queue_cell_evaluation", %{"cell_id" => "setup"})
assert_receive {:operation, {:set_runtime, _pid, %{} = _runtime}}
@ -150,11 +150,11 @@ defmodule LivebookWeb.SessionLiveTest do
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
view
|> element(~s{[data-element="session"]})
|> element(~s{[data-el-session]})
|> render_hook("queue_cell_evaluation", %{"cell_id" => cell_id})
view
|> element(~s{[data-element="session"]})
|> element(~s{[data-el-session]})
|> render_hook("cancel_cell_evaluation", %{"cell_id" => cell_id})
assert %{cell_infos: %{^cell_id => %{eval: %{status: :ready}}}} =
@ -168,7 +168,7 @@ defmodule LivebookWeb.SessionLiveTest do
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
view
|> element(~s{[data-element="session"]})
|> element(~s{[data-el-session]})
|> render_hook("insert_cell_below", %{"cell_id" => cell_id, "type" => "markdown"})
assert %{notebook: %{sections: [%{cells: [_first_cell, %Cell.Markdown{}]}]}} =
@ -182,7 +182,7 @@ defmodule LivebookWeb.SessionLiveTest do
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
view
|> element(~s{[data-element="session"]})
|> element(~s{[data-el-session]})
|> render_hook("insert_cell_below", %{"section_id" => section_id, "type" => "markdown"})
assert %{notebook: %{sections: [%{cells: [%Cell.Markdown{}, _first_cell]}]}} =
@ -196,7 +196,7 @@ defmodule LivebookWeb.SessionLiveTest do
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
view
|> element(~s{[data-element="session"]})
|> element(~s{[data-el-session]})
|> render_hook("delete_cell", %{"cell_id" => cell_id})
assert %{notebook: %{sections: [%{cells: []}]}} = Session.get_data(session.pid)
@ -245,7 +245,7 @@ defmodule LivebookWeb.SessionLiveTest do
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
view
|> element(~s/[data-element="outputs-container"] form/)
|> element(~s/[data-el-outputs-container] form/)
|> render_change(%{"value" => "10"})
assert %{input_values: %{"input1" => 10}} = Session.get_data(session.pid)
@ -274,7 +274,7 @@ defmodule LivebookWeb.SessionLiveTest do
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
view
|> element(~s/[data-element="outputs-container"] form/)
|> element(~s/[data-el-outputs-container] form/)
|> render_change(%{"value" => "line\r\nline"})
assert %{input_values: %{"input1" => "line\nline"}} = Session.get_data(session.pid)
@ -312,7 +312,7 @@ defmodule LivebookWeb.SessionLiveTest do
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
view
|> element(~s/[data-element="outputs-container"] form/)
|> element(~s/[data-el-outputs-container] form/)
|> render_change(%{"value" => "sherlock"})
# The new value is on the page
@ -321,7 +321,7 @@ defmodule LivebookWeb.SessionLiveTest do
assert %{input_values: %{"input1" => "initial"}} = Session.get_data(session.pid)
view
|> element(~s/[data-element="outputs-container"] button/, "Send")
|> element(~s/[data-el-outputs-container] button/, "Send")
|> render_click()
assert_receive {:event, :form_ref, %{data: %{name: "sherlock"}, type: :submit}}
@ -512,7 +512,7 @@ defmodule LivebookWeb.SessionLiveTest do
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
view
|> element(~s{[data-element="session"]})
|> element(~s{[data-el-session]})
|> render_hook("intellisense_request", %{
"cell_id" => cell_id,
"type" => "completion",
@ -534,7 +534,7 @@ defmodule LivebookWeb.SessionLiveTest do
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
view
|> element(~s{[data-element="session"]})
|> element(~s{[data-el-session]})
|> render_hook("intellisense_request", %{
"cell_id" => cell_id,
"type" => "completion",

View file

@ -24,7 +24,7 @@ defmodule Livebook.Runtime.NoopRuntime do
def evaluate_code(_, _, _, _, _ \\ []), do: :ok
def forget_evaluation(_, _), do: :ok
def drop_container(_, _), do: :ok
def handle_intellisense(_, _, _, _, _), do: :ok
def handle_intellisense(_, _, _, _), do: make_ref()
def standalone?(_), do: false
def read_file(_, _), do: raise("not implemented")